mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
[Serverless Search] Fix connectors bugs (#170679)
## Summary This includes a few bug fixes and improvements for connectors in Serverless. 1) Syncs now set a job type so connectors will actually run them 2) Callouts to tell the user that a sync has been scheduled 3) Fix some wonky refetch behavior on updates 4) Consistent error toasts --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
ea7ae45028
commit
550b3cf08d
14 changed files with 321 additions and 148 deletions
|
@ -74,7 +74,7 @@ export const startConnectorSync = async (
|
|||
error: null,
|
||||
indexed_document_count: 0,
|
||||
indexed_document_volume: 0,
|
||||
job_type: jobType,
|
||||
job_type: jobType || SyncJobType.FULL,
|
||||
last_seen: null,
|
||||
metadata: {},
|
||||
started_at: null,
|
||||
|
|
|
@ -9,27 +9,30 @@
|
|||
import { ElasticsearchClient } from '@kbn/core/server';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import { CONNECTORS_INDEX } from '..';
|
||||
import { CONNECTORS_INDEX, fetchConnectorById } from '..';
|
||||
|
||||
import { ConnectorDocument } from '../types/connectors';
|
||||
import { ConnectorDocument, ConnectorStatus } from '../types/connectors';
|
||||
|
||||
export const updateConnectorServiceType = async (
|
||||
client: ElasticsearchClient,
|
||||
connectorId: string,
|
||||
serviceType: string
|
||||
) => {
|
||||
const connectorResult = await client.get<ConnectorDocument>({
|
||||
id: connectorId,
|
||||
index: CONNECTORS_INDEX,
|
||||
});
|
||||
const connector = connectorResult._source;
|
||||
if (connector) {
|
||||
const connectorResult = await fetchConnectorById(client, connectorId);
|
||||
|
||||
if (connectorResult?.value) {
|
||||
const result = await client.index<ConnectorDocument>({
|
||||
document: { ...connector, service_type: serviceType },
|
||||
document: {
|
||||
...connectorResult.value,
|
||||
configuration: {},
|
||||
service_type: serviceType,
|
||||
status: ConnectorStatus.NEEDS_CONFIGURATION,
|
||||
},
|
||||
id: connectorId,
|
||||
index: CONNECTORS_INDEX,
|
||||
if_seq_no: connectorResult.seqNo,
|
||||
if_primary_term: connectorResult.primaryTerm,
|
||||
});
|
||||
await client.indices.refresh({ index: CONNECTORS_INDEX });
|
||||
return result;
|
||||
} else {
|
||||
throw new Error(
|
||||
|
|
|
@ -6,12 +6,7 @@
|
|||
*/
|
||||
import React, { useEffect, useState } from 'react';
|
||||
|
||||
import {
|
||||
Connector,
|
||||
ConnectorStatus,
|
||||
pageToPagination,
|
||||
SyncJobsTable,
|
||||
} from '@kbn/search-connectors';
|
||||
import { Connector, ConnectorStatus } from '@kbn/search-connectors';
|
||||
import {
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
|
@ -20,15 +15,14 @@ import {
|
|||
EuiStepsHorizontalProps,
|
||||
EuiTabbedContent,
|
||||
EuiTabbedContentTab,
|
||||
Pagination,
|
||||
} from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { CONFIGURATION_LABEL, OVERVIEW_LABEL } from '../../../../../common/i18n_string';
|
||||
import { ConnectorLinkElasticsearch } from './connector_link';
|
||||
import { ConnectorConfigFields } from './connector_config_fields';
|
||||
import { ConnectorIndexName } from './connector_index_name';
|
||||
import { useSyncJobs } from '../../../hooks/api/use_sync_jobs';
|
||||
import { ConnectorConfigurationPanels } from './connector_config_panels';
|
||||
import { ConnectorOverview } from './connector_overview';
|
||||
|
||||
interface ConnectorConfigurationProps {
|
||||
connector: Connector;
|
||||
|
@ -91,31 +85,10 @@ export const ConnectorConfiguration: React.FC<ConnectorConfigurationProps> = ({
|
|||
size: 's',
|
||||
},
|
||||
];
|
||||
const [pagination, setPagination] = useState<Omit<Pagination, 'totalItemCount'>>({
|
||||
pageIndex: 0,
|
||||
pageSize: 20,
|
||||
});
|
||||
|
||||
const { data: syncJobsData, isLoading: syncJobsLoading } = useSyncJobs(connector.id, pagination);
|
||||
|
||||
const tabs: EuiTabbedContentTab[] = [
|
||||
{
|
||||
content: (
|
||||
<>
|
||||
<EuiSpacer />
|
||||
<SyncJobsTable
|
||||
isLoading={syncJobsLoading}
|
||||
onPaginate={({ page }) => setPagination({ pageIndex: page.index, pageSize: page.size })}
|
||||
pagination={
|
||||
syncJobsData
|
||||
? pageToPagination(syncJobsData?._meta.page)
|
||||
: { pageIndex: 0, pageSize: 20, totalItemCount: 0 }
|
||||
}
|
||||
syncJobs={syncJobsData?.data || []}
|
||||
type="content"
|
||||
/>
|
||||
</>
|
||||
),
|
||||
content: <ConnectorOverview connector={connector} />,
|
||||
id: 'overview',
|
||||
name: OVERVIEW_LABEL,
|
||||
},
|
||||
|
|
|
@ -7,8 +7,8 @@
|
|||
|
||||
import { EuiButton, EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiText, EuiTitle } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { Connector } from '@kbn/search-connectors';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Connector, ConnectorStatus } from '@kbn/search-connectors';
|
||||
import React, { useState } from 'react';
|
||||
import { useQueryClient, useMutation } from '@tanstack/react-query';
|
||||
import { isValidIndexName } from '../../../../utils/validate_index_name';
|
||||
import { SAVE_LABEL } from '../../../../../common/i18n_string';
|
||||
|
@ -16,16 +16,19 @@ import { useConnector } from '../../../hooks/api/use_connector';
|
|||
import { useKibanaServices } from '../../../hooks/use_kibana';
|
||||
import { ApiKeyPanel } from './api_key_panel';
|
||||
import { ConnectorIndexNameForm } from './connector_index_name_form';
|
||||
import { useShowErrorToast } from '../../../hooks/use_error_toast';
|
||||
import { SyncScheduledCallOut } from './sync_scheduled_callout';
|
||||
|
||||
interface ConnectorIndexNameProps {
|
||||
connector: Connector;
|
||||
}
|
||||
|
||||
export const ConnectorIndexName: React.FC<ConnectorIndexNameProps> = ({ connector }) => {
|
||||
const { http, notifications } = useKibanaServices();
|
||||
const { http } = useKibanaServices();
|
||||
const queryClient = useQueryClient();
|
||||
const { queryKey } = useConnector(connector.id);
|
||||
const { data, error, isLoading, isSuccess, mutate, reset } = useMutation({
|
||||
const showErrorToast = useShowErrorToast();
|
||||
const { data, isLoading, isSuccess, mutate } = useMutation({
|
||||
mutationFn: async ({ inputName, sync }: { inputName: string | null; sync?: boolean }) => {
|
||||
if (inputName && inputName !== connector.index_name) {
|
||||
const body = { index_name: inputName };
|
||||
|
@ -38,25 +41,18 @@ export const ConnectorIndexName: React.FC<ConnectorIndexNameProps> = ({ connecto
|
|||
}
|
||||
return inputName;
|
||||
},
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (isSuccess) {
|
||||
onError: (error) =>
|
||||
showErrorToast(
|
||||
error,
|
||||
i18n.translate('xpack.serverlessSearch.connectors.config.connectorIndexNameError', {
|
||||
defaultMessage: 'Error updating index name',
|
||||
})
|
||||
),
|
||||
onSuccess: () => {
|
||||
queryClient.setQueryData(queryKey, { connector: { ...connector, index_name: data } });
|
||||
queryClient.invalidateQueries(queryKey);
|
||||
reset();
|
||||
}
|
||||
}, [data, isSuccess, connector, queryClient, queryKey, reset]);
|
||||
|
||||
useEffect(() => {
|
||||
if (error) {
|
||||
notifications.toasts.addError(error as Error, {
|
||||
title: i18n.translate('xpack.serverlessSearch.connectors.config.connectorIndexNameError', {
|
||||
defaultMessage: 'Error updating index name',
|
||||
}),
|
||||
});
|
||||
}
|
||||
}, [error, notifications]);
|
||||
},
|
||||
});
|
||||
|
||||
const [newIndexName, setNewIndexname] = useState(connector.index_name);
|
||||
|
||||
|
@ -109,7 +105,12 @@ export const ConnectorIndexName: React.FC<ConnectorIndexNameProps> = ({ connecto
|
|||
<span>
|
||||
<EuiButton
|
||||
color="primary"
|
||||
disabled={!isValidIndexName(newIndexName)}
|
||||
disabled={
|
||||
!(
|
||||
isValidIndexName(newIndexName) &&
|
||||
[ConnectorStatus.CONFIGURED, ConnectorStatus.CONNECTED].includes(connector.status)
|
||||
)
|
||||
}
|
||||
fill
|
||||
isLoading={isLoading}
|
||||
onClick={() => mutate({ inputName: newIndexName, sync: true })}
|
||||
|
@ -121,6 +122,12 @@ export const ConnectorIndexName: React.FC<ConnectorIndexNameProps> = ({ connecto
|
|||
</span>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
{isSuccess && (
|
||||
<>
|
||||
<EuiSpacer />
|
||||
<SyncScheduledCallOut />
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -17,10 +17,12 @@ import {
|
|||
EuiTitle,
|
||||
} from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { ELASTICSEARCH_URL_PLACEHOLDER } from '@kbn/search-api-panels/constants';
|
||||
import { ConnectorStatus } from '@kbn/search-connectors';
|
||||
import React from 'react';
|
||||
import { docLinks } from '../../../../../common/doc_links';
|
||||
import { useAssetBasePath } from '../../../hooks/use_asset_base_path';
|
||||
import { useKibanaServices } from '../../../hooks/use_kibana';
|
||||
|
||||
interface ConnectorLinkElasticsearchProps {
|
||||
connectorId: string;
|
||||
|
@ -34,6 +36,10 @@ export const ConnectorLinkElasticsearch: React.FC<ConnectorLinkElasticsearchProp
|
|||
status,
|
||||
}) => {
|
||||
const assetBasePath = useAssetBasePath();
|
||||
const { cloud } = useKibanaServices();
|
||||
|
||||
const elasticsearchUrl = cloud?.elasticsearchUrl ?? ELASTICSEARCH_URL_PLACEHOLDER;
|
||||
|
||||
return (
|
||||
<EuiFlexGroup direction="column" alignItems="center" justifyContent="center">
|
||||
<EuiFlexItem>
|
||||
|
@ -111,6 +117,17 @@ export const ConnectorLinkElasticsearch: React.FC<ConnectorLinkElasticsearchProp
|
|||
{Boolean(serviceType) && <EuiCode>{serviceType}</EuiCode>}
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
<EuiSpacer />
|
||||
<EuiFlexGroup justifyContent="spaceBetween" alignItems="center">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiText size="s">
|
||||
<strong>elasticsearch.host</strong>
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiCode>{elasticsearchUrl}</EuiCode>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
{status === ConnectorStatus.CREATED && (
|
||||
<>
|
||||
<EuiSpacer />
|
||||
|
|
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
* 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, EuiSpacer, Pagination } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import {
|
||||
Connector,
|
||||
ConnectorStatus,
|
||||
pageToPagination,
|
||||
SyncJobsTable,
|
||||
} from '@kbn/search-connectors';
|
||||
import { useQueryClient, useMutation } from '@tanstack/react-query';
|
||||
import React, { useState } from 'react';
|
||||
import { useConnector } from '../../../hooks/api/use_connector';
|
||||
import { useSyncJobs } from '../../../hooks/api/use_sync_jobs';
|
||||
import { useShowErrorToast } from '../../../hooks/use_error_toast';
|
||||
import { useKibanaServices } from '../../../hooks/use_kibana';
|
||||
import { SyncScheduledCallOut } from './sync_scheduled_callout';
|
||||
|
||||
interface ConnectorOverviewProps {
|
||||
connector: Connector;
|
||||
}
|
||||
|
||||
export const ConnectorOverview: React.FC<ConnectorOverviewProps> = ({ connector }) => {
|
||||
const { http } = useKibanaServices();
|
||||
const queryClient = useQueryClient();
|
||||
const { queryKey } = useConnector(connector.id);
|
||||
const showErrorToast = useShowErrorToast();
|
||||
const { data, isLoading, isSuccess, mutate } = useMutation({
|
||||
mutationFn: async () => {
|
||||
await http.post(`/internal/serverless_search/connectors/${connector.id}/sync`);
|
||||
},
|
||||
onError: (error) =>
|
||||
showErrorToast(
|
||||
error,
|
||||
i18n.translate('xpack.serverlessSearch.connectors.config.connectorSyncError', {
|
||||
defaultMessage: 'Error scheduling sync',
|
||||
})
|
||||
),
|
||||
onSuccess: () => {
|
||||
queryClient.setQueryData(queryKey, { connector: { ...connector, index_name: data } });
|
||||
queryClient.invalidateQueries(queryKey);
|
||||
},
|
||||
});
|
||||
|
||||
const [pagination, setPagination] = useState<Omit<Pagination, 'totalItemCount'>>({
|
||||
pageIndex: 0,
|
||||
pageSize: 20,
|
||||
});
|
||||
|
||||
const { data: syncJobsData, isLoading: syncJobsLoading } = useSyncJobs(connector.id, pagination);
|
||||
|
||||
return (
|
||||
<>
|
||||
<EuiSpacer />
|
||||
<SyncJobsTable
|
||||
isLoading={syncJobsLoading}
|
||||
onPaginate={({ page }) => setPagination({ pageIndex: page.index, pageSize: page.size })}
|
||||
pagination={
|
||||
syncJobsData
|
||||
? pageToPagination(syncJobsData?._meta.page)
|
||||
: { pageIndex: 0, pageSize: 20, totalItemCount: 0 }
|
||||
}
|
||||
syncJobs={syncJobsData?.data || []}
|
||||
type="content"
|
||||
/>
|
||||
<EuiSpacer />
|
||||
<span>
|
||||
<EuiButton
|
||||
color="primary"
|
||||
disabled={
|
||||
![ConnectorStatus.CONFIGURED, ConnectorStatus.CONNECTED].includes(connector.status)
|
||||
}
|
||||
fill
|
||||
isLoading={isLoading}
|
||||
onClick={() => mutate()}
|
||||
>
|
||||
{i18n.translate('xpack.serverlessSearch.connectors.config.syncLabel', {
|
||||
defaultMessage: 'Sync',
|
||||
})}
|
||||
</EuiButton>
|
||||
</span>
|
||||
{isSuccess && (
|
||||
<>
|
||||
<EuiSpacer />
|
||||
<SyncScheduledCallOut />
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* 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 { EuiCallOut, EuiText } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import React from 'react';
|
||||
|
||||
export const SyncScheduledCallOut: React.FC = () => {
|
||||
return (
|
||||
<EuiCallOut
|
||||
color="primary"
|
||||
iconType="iInCircle"
|
||||
title={i18n.translate('xpack.serverlessSearch.connectors.syncScheduledTitle', {
|
||||
defaultMessage: 'A sync has been scheduled',
|
||||
})}
|
||||
>
|
||||
<EuiText>
|
||||
{i18n.translate('xpack.serverlessSearch.connectors.syncSheduledDescription', {
|
||||
defaultMessage:
|
||||
'It may take a minute for this sync to be visible and for the connector to pick it up',
|
||||
})}
|
||||
</EuiText>
|
||||
</EuiCallOut>
|
||||
);
|
||||
};
|
|
@ -44,7 +44,7 @@ export const EditConnector: React.FC = () => {
|
|||
application: { navigateToUrl },
|
||||
} = useKibanaServices();
|
||||
|
||||
const { data, isLoading, refetch } = useConnector(id);
|
||||
const { data, isLoading } = useConnector(id);
|
||||
|
||||
if (isLoading) {
|
||||
<EuiPageTemplate offset={0} grow restrictWidth data-test-subj="svlSearchEditConnectorsPage">
|
||||
|
@ -91,7 +91,7 @@ export const EditConnector: React.FC = () => {
|
|||
<EuiText size="s">{CONNECTOR_LABEL}</EuiText>
|
||||
<EuiFlexGroup direction="row" justifyContent="spaceBetween">
|
||||
<EuiFlexItem>
|
||||
<EditName connectorId={id} name={connector.name} onSuccess={refetch} />
|
||||
<EditName connector={connector} />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
{deleteModalIsOpen && (
|
||||
|
@ -151,17 +151,9 @@ export const EditConnector: React.FC = () => {
|
|||
<EuiPageTemplate.Section>
|
||||
<EuiFlexGroup direction="row">
|
||||
<EuiFlexItem grow={1}>
|
||||
<EditServiceType
|
||||
connectorId={id}
|
||||
serviceType={connector.service_type ?? ''}
|
||||
onSuccess={() => refetch()}
|
||||
/>
|
||||
<EditServiceType connector={connector} />
|
||||
<EuiSpacer />
|
||||
<EditDescription
|
||||
connectorId={id}
|
||||
description={connector.description ?? ''}
|
||||
onSuccess={refetch}
|
||||
/>
|
||||
<EditDescription connector={connector} />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={2}>
|
||||
<EuiPanel hasBorder hasShadow={false}>
|
||||
|
|
|
@ -19,47 +19,51 @@ import {
|
|||
EuiText,
|
||||
EuiButtonEmpty,
|
||||
} from '@elastic/eui';
|
||||
import { useMutation } from '@tanstack/react-query';
|
||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { Connector } from '@kbn/search-connectors';
|
||||
import { CANCEL_LABEL, EDIT_LABEL, SAVE_LABEL } from '../../../../common/i18n_string';
|
||||
import { useKibanaServices } from '../../hooks/use_kibana';
|
||||
import { useConnector } from '../../hooks/api/use_connector';
|
||||
import { useShowErrorToast } from '../../hooks/use_error_toast';
|
||||
|
||||
interface EditDescriptionProps {
|
||||
connectorId: string;
|
||||
description: string;
|
||||
onSuccess: () => void;
|
||||
connector: Connector;
|
||||
}
|
||||
|
||||
export const EditDescription: React.FC<EditDescriptionProps> = ({
|
||||
connectorId,
|
||||
description,
|
||||
onSuccess,
|
||||
}) => {
|
||||
export const EditDescription: React.FC<EditDescriptionProps> = ({ connector }) => {
|
||||
const [isEditing, setIsEditing] = useState(false);
|
||||
const [newDescription, setNewDescription] = useState(description);
|
||||
const [newDescription, setNewDescription] = useState(connector.description || '');
|
||||
const { http } = useKibanaServices();
|
||||
const showErrorToast = useShowErrorToast();
|
||||
const queryClient = useQueryClient();
|
||||
const { queryKey } = useConnector(connector.id);
|
||||
|
||||
useEffect(() => setNewDescription(description), [description]);
|
||||
useEffect(() => setNewDescription(connector.description || ''), [connector.description]);
|
||||
|
||||
const { isLoading, isSuccess, mutate } = useMutation({
|
||||
const { isLoading, 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;
|
||||
await http.post(`/internal/serverless_search/connectors/${connector.id}/description`, {
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
return inputDescription;
|
||||
},
|
||||
onError: (error) =>
|
||||
showErrorToast(
|
||||
error,
|
||||
i18n.translate('xpack.serverlessSearch.connectors.config.connectorDescription', {
|
||||
defaultMessage: 'Error updating description',
|
||||
})
|
||||
),
|
||||
onSuccess: (successData) => {
|
||||
queryClient.setQueryData(queryKey, {
|
||||
connector: { ...connector, description: successData },
|
||||
});
|
||||
queryClient.invalidateQueries(queryKey);
|
||||
setIsEditing(false);
|
||||
},
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (isSuccess) {
|
||||
setIsEditing(false);
|
||||
onSuccess();
|
||||
}
|
||||
}, [isSuccess, onSuccess]);
|
||||
|
||||
return (
|
||||
<EuiFlexGroup direction="row">
|
||||
<EuiForm>
|
||||
|
@ -80,10 +84,10 @@ export const EditDescription: React.FC<EditDescriptionProps> = ({
|
|||
{isEditing ? (
|
||||
<EuiFieldText
|
||||
onChange={(event) => setNewDescription(event.target.value)}
|
||||
value={newDescription}
|
||||
value={newDescription || ''}
|
||||
/>
|
||||
) : (
|
||||
<EuiText size="s">{description}</EuiText>
|
||||
<EuiText size="s">{connector.description}</EuiText>
|
||||
)}
|
||||
</EuiFormRow>
|
||||
</EuiFlexItem>
|
||||
|
@ -118,7 +122,7 @@ export const EditDescription: React.FC<EditDescriptionProps> = ({
|
|||
size="s"
|
||||
isLoading={isLoading}
|
||||
onClick={() => {
|
||||
setNewDescription(description);
|
||||
setNewDescription(connector.description || '');
|
||||
setIsEditing(false);
|
||||
}}
|
||||
>
|
||||
|
|
|
@ -19,47 +19,58 @@ import {
|
|||
EuiFormLabel,
|
||||
EuiSpacer,
|
||||
} from '@elastic/eui';
|
||||
import { useMutation } from '@tanstack/react-query';
|
||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { Connector } from '@kbn/search-connectors';
|
||||
import { CANCEL_LABEL, CONNECTOR_LABEL, SAVE_LABEL } from '../../../../common/i18n_string';
|
||||
import { useKibanaServices } from '../../hooks/use_kibana';
|
||||
import { useConnector } from '../../hooks/api/use_connector';
|
||||
import { useShowErrorToast } from '../../hooks/use_error_toast';
|
||||
|
||||
interface EditNameProps {
|
||||
connectorId: string;
|
||||
name: string;
|
||||
onSuccess: () => void;
|
||||
connector: Connector;
|
||||
}
|
||||
|
||||
export const EditName: React.FC<EditNameProps> = ({ connectorId, name, onSuccess }) => {
|
||||
export const EditName: React.FC<EditNameProps> = ({ connector }) => {
|
||||
const [isEditing, setIsEditing] = useState(false);
|
||||
const [newName, setNewName] = useState(name);
|
||||
const [newName, setNewName] = useState(connector.name || CONNECTOR_LABEL);
|
||||
const { http } = useKibanaServices();
|
||||
const showErrorToast = useShowErrorToast();
|
||||
const queryClient = useQueryClient();
|
||||
const { queryKey } = useConnector(connector.id);
|
||||
|
||||
useEffect(() => setNewName(name), [name]);
|
||||
useEffect(() => setNewName(connector.name), [connector.name]);
|
||||
|
||||
const { isLoading, isSuccess, mutate } = useMutation({
|
||||
const { isLoading, mutate } = useMutation({
|
||||
mutationFn: async (inputName: string) => {
|
||||
const body = { name: inputName };
|
||||
const result = await http.post(`/internal/serverless_search/connectors/${connectorId}/name`, {
|
||||
await http.post(`/internal/serverless_search/connectors/${connector.id}/name`, {
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
return result;
|
||||
return inputName;
|
||||
},
|
||||
onError: (error) =>
|
||||
showErrorToast(
|
||||
error,
|
||||
i18n.translate('xpack.serverlessSearch.connectors.config.connectorNameError', {
|
||||
defaultMessage: 'Error updating name',
|
||||
})
|
||||
),
|
||||
onSuccess: (successData) => {
|
||||
queryClient.setQueryData(queryKey, {
|
||||
connector: { ...connector, service_type: successData },
|
||||
});
|
||||
queryClient.invalidateQueries(queryKey);
|
||||
setIsEditing(false);
|
||||
},
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (isSuccess) {
|
||||
setIsEditing(false);
|
||||
onSuccess();
|
||||
}
|
||||
}, [isSuccess, onSuccess]);
|
||||
|
||||
return (
|
||||
<EuiFlexGroup direction="row">
|
||||
{!isEditing ? (
|
||||
<>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiTitle>
|
||||
<h1>{name || CONNECTOR_LABEL}</h1>
|
||||
<h1>{connector.name || CONNECTOR_LABEL}</h1>
|
||||
</EuiTitle>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem
|
||||
|
@ -117,7 +128,7 @@ export const EditName: React.FC<EditNameProps> = ({ connectorId, name, onSuccess
|
|||
size="s"
|
||||
isLoading={isLoading}
|
||||
onClick={() => {
|
||||
setNewName(name);
|
||||
setNewName(connector.name);
|
||||
setIsEditing(false);
|
||||
}}
|
||||
>
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import React, { useEffect } from 'react';
|
||||
import React from 'react';
|
||||
import {
|
||||
EuiFlexItem,
|
||||
EuiFlexGroup,
|
||||
|
@ -15,23 +15,23 @@ import {
|
|||
EuiIcon,
|
||||
EuiSuperSelect,
|
||||
} from '@elastic/eui';
|
||||
import { useMutation } from '@tanstack/react-query';
|
||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { Connector } from '@kbn/search-connectors';
|
||||
import { useKibanaServices } from '../../hooks/use_kibana';
|
||||
import { useConnectorTypes } from '../../hooks/api/use_connector_types';
|
||||
import { useShowErrorToast } from '../../hooks/use_error_toast';
|
||||
import { useConnector } from '../../hooks/api/use_connector';
|
||||
|
||||
interface EditServiceTypeProps {
|
||||
connectorId: string;
|
||||
serviceType: string;
|
||||
onSuccess: () => void;
|
||||
connector: Connector;
|
||||
}
|
||||
|
||||
export const EditServiceType: React.FC<EditServiceTypeProps> = ({
|
||||
connectorId,
|
||||
serviceType,
|
||||
onSuccess,
|
||||
}) => {
|
||||
export const EditServiceType: React.FC<EditServiceTypeProps> = ({ connector }) => {
|
||||
const { http } = useKibanaServices();
|
||||
const { data: connectorTypes } = useConnectorTypes();
|
||||
const showErrorToast = useShowErrorToast();
|
||||
const queryClient = useQueryClient();
|
||||
const { queryKey } = useConnector(connector.id);
|
||||
|
||||
const options =
|
||||
connectorTypes?.connectors.map((connectorType) => ({
|
||||
|
@ -51,25 +51,29 @@ export const EditServiceType: React.FC<EditServiceTypeProps> = ({
|
|||
value: connectorType.serviceType,
|
||||
})) || [];
|
||||
|
||||
const { isLoading, isSuccess, mutate } = useMutation({
|
||||
const { isLoading, 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;
|
||||
await http.post(`/internal/serverless_search/connectors/${connector.id}/service_type`, {
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
return inputServiceType;
|
||||
},
|
||||
onError: (error) =>
|
||||
showErrorToast(
|
||||
error,
|
||||
i18n.translate('xpack.serverlessSearch.connectors.config.connectorServiceTypeError', {
|
||||
defaultMessage: 'Error updating service type',
|
||||
})
|
||||
),
|
||||
onSuccess: (successData) => {
|
||||
queryClient.setQueryData(queryKey, {
|
||||
connector: { ...connector, service_type: successData },
|
||||
});
|
||||
queryClient.invalidateQueries(queryKey);
|
||||
},
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (isSuccess) {
|
||||
onSuccess();
|
||||
}
|
||||
}, [isSuccess, onSuccess]);
|
||||
|
||||
return (
|
||||
<EuiForm>
|
||||
<EuiFormLabel>
|
||||
|
@ -81,7 +85,7 @@ export const EditServiceType: React.FC<EditServiceTypeProps> = ({
|
|||
isLoading={isLoading}
|
||||
onChange={(event) => mutate(event)}
|
||||
options={options}
|
||||
valueOfSelected={serviceType ?? ''}
|
||||
valueOfSelected={connector.service_type || undefined}
|
||||
/>
|
||||
</EuiForm>
|
||||
);
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* 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 { getErrorMessage } from '../../utils/get_error_message';
|
||||
import { useKibanaServices } from './use_kibana';
|
||||
|
||||
export const useShowErrorToast = () => {
|
||||
const { notifications } = useKibanaServices();
|
||||
|
||||
return (error: unknown, errorTitle?: string) =>
|
||||
notifications.toasts.addError(new Error(getErrorMessage(error)), {
|
||||
title: errorTitle || getErrorMessage(error),
|
||||
});
|
||||
};
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* 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 { KibanaServerError } from '@kbn/kibana-utils-plugin/common';
|
||||
|
||||
export function getErrorMessage(error: unknown): string {
|
||||
if (typeof error === 'string') {
|
||||
return error;
|
||||
}
|
||||
if (typeof error === 'object') {
|
||||
return (error as { body: KibanaServerError })?.body?.message || '';
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
|
@ -35,5 +35,6 @@
|
|||
"@kbn/react-kibana-context-theme",
|
||||
"@kbn/search-connectors",
|
||||
"@kbn/shared-ux-router",
|
||||
"@kbn/kibana-utils-plugin",
|
||||
]
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue