mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
[Connectors] Adapt Connectors UI for agentless (#202179)
## Summary Couple of changes to support Elastic-managed connectors in 9.x ### Video overview https://github.com/user-attachments/assets/086ae96a-0520-483e-b055-5e672b3f65f5 ### List of changes 1. Elatic-managed connectors now enforce `content-` prefix <img width="400" alt="Screenshot 2024-11-28 at 14 55 27" src="https://github.com/user-attachments/assets/aa7f6d6d-39af-42ad-b5f1-5455e37006df"> 2. Banner about not attached index changed to warning (yellow) instead of danger (red) - as this is not an error state <img width="400" alt="Screenshot 2024-11-28 at 14 54 48" src="https://github.com/user-attachments/assets/00d5c332-0366-4420-8934-32404b4fb1c3"> 3. Get rid of native connector API keys from UI - as they won't work anyway without ent-search node <img width="400" alt="Screenshot 2024-11-28 at 14 56 22" src="https://github.com/user-attachments/assets/6fcea6b4-0559-4419-83d6-4a9db9a71c88"> 4. Index name generation for native connectors, `content-` is always added as prefix for native <img width="400" alt="Screenshot 2024-11-28 at 14 57 01" src="https://github.com/user-attachments/assets/c17ccc35-1d91-4666-8db5-b796685c928e"> ### Checklist Check the PR satisfies following conditions. Reviewers should verify this PR satisfies this list as well. - [x] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md) - [x] [Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html) was added for features that require explanation or tutorials - [x] This was checked for breaking HTTP API changes, and any breaking changes have been approved by the breaking-change committee. The `release_note:breaking` label should be applied in these situations. - [x] [Flaky Test Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was used on any tests changed - [x] The PR description includes the appropriate Release Notes section, and the correct `release_note:*` label is applied per the [guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process) --------- Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com> Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
fd589b844c
commit
89063df988
13 changed files with 180 additions and 107 deletions
|
@ -15,6 +15,8 @@ import {
|
|||
|
||||
import { docLinks } from './doc_links';
|
||||
|
||||
export const MANAGED_CONNECTOR_INDEX_PREFIX = 'content-';
|
||||
|
||||
// needs to be a function because, docLinks are only populated with actual
|
||||
// documentation links in browser after SearchConnectorsPlugin starts
|
||||
export const getConnectorsDict = (): Record<string, ConnectorClientSideDefinition> => ({
|
||||
|
|
|
@ -10,6 +10,7 @@ import { HttpLogic } from '../../../shared/http';
|
|||
export interface GenerateConnectorNamesApiArgs {
|
||||
connectorName?: string;
|
||||
connectorType?: string;
|
||||
isManagedConnector?: boolean;
|
||||
}
|
||||
|
||||
export interface GenerateConnectorNamesApiResponse {
|
||||
|
@ -19,14 +20,16 @@ export interface GenerateConnectorNamesApiResponse {
|
|||
}
|
||||
|
||||
export const generateConnectorNames = async (
|
||||
{ connectorType, connectorName }: GenerateConnectorNamesApiArgs = { connectorType: 'custom' }
|
||||
{ connectorType, connectorName, isManagedConnector }: GenerateConnectorNamesApiArgs = {
|
||||
connectorType: 'custom',
|
||||
}
|
||||
) => {
|
||||
if (connectorType === '') {
|
||||
connectorType = 'custom';
|
||||
}
|
||||
const route = `/internal/enterprise_search/connectors/generate_connector_name`;
|
||||
return await HttpLogic.values.http.post(route, {
|
||||
body: JSON.stringify({ connectorName, connectorType }),
|
||||
body: JSON.stringify({ connectorName, connectorType, isManagedConnector }),
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ import {
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
|
||||
import { Connector } from '@kbn/search-connectors';
|
||||
import { Connector, MANAGED_CONNECTOR_INDEX_PREFIX } from '@kbn/search-connectors';
|
||||
|
||||
import { Status } from '../../../../../common/types/api';
|
||||
|
||||
|
@ -65,66 +65,114 @@ export const AttachIndexBox: React.FC<AttachIndexBoxProps> = ({ connector }) =>
|
|||
createApiError,
|
||||
attachApiError,
|
||||
} = useValues(AttachIndexLogic);
|
||||
const [selectedIndex, setSelectedIndex] = useState<
|
||||
{ label: string; shouldCreate?: boolean } | undefined
|
||||
>(
|
||||
connector.index_name
|
||||
? {
|
||||
label: connector.index_name,
|
||||
}
|
||||
: undefined
|
||||
);
|
||||
const [selectedLanguage] = useState<string>();
|
||||
const [query, setQuery] = useState<{
|
||||
isFullMatch: boolean;
|
||||
searchValue: string;
|
||||
}>();
|
||||
const [sanitizedName, setSanitizedName] = useState<string>(formatApiName(connector.name));
|
||||
|
||||
const { makeRequest } = useActions(FetchAvailableIndicesAPILogic);
|
||||
const { data, status } = useValues(FetchAvailableIndicesAPILogic);
|
||||
const isLoading = [Status.IDLE, Status.LOADING].includes(status);
|
||||
|
||||
// Helper function to remove the managed connector index prefix from the index name
|
||||
const removePrefixConnectorIndex = (connectorIndexName: string) => {
|
||||
if (!connector.is_native) {
|
||||
return connectorIndexName;
|
||||
}
|
||||
if (connectorIndexName.startsWith(MANAGED_CONNECTOR_INDEX_PREFIX)) {
|
||||
return connectorIndexName.substring(MANAGED_CONNECTOR_INDEX_PREFIX.length);
|
||||
}
|
||||
return connectorIndexName;
|
||||
};
|
||||
|
||||
// Helper function to add the managed connector index prefix to the index name
|
||||
const prefixConnectorIndex = (connectorIndexName: string) => {
|
||||
if (!connector.is_native) {
|
||||
return connectorIndexName;
|
||||
}
|
||||
if (connectorIndexName.startsWith(MANAGED_CONNECTOR_INDEX_PREFIX)) {
|
||||
return connectorIndexName;
|
||||
}
|
||||
return `${MANAGED_CONNECTOR_INDEX_PREFIX}${connectorIndexName}`;
|
||||
};
|
||||
|
||||
const [query, setQuery] = useState<{
|
||||
isFullMatch: boolean;
|
||||
searchValue: string;
|
||||
}>();
|
||||
const [sanitizedName, setSanitizedName] = useState<string>(
|
||||
prefixConnectorIndex(formatApiName(connector.name))
|
||||
);
|
||||
|
||||
const [selectedIndex, setSelectedIndex] = useState<
|
||||
{ label: string; shouldCreate?: boolean } | undefined
|
||||
>(
|
||||
// For managed connectors, the index name should be displayed without prefix
|
||||
// As `content-` is fixed UI element
|
||||
connector.index_name
|
||||
? {
|
||||
label: removePrefixConnectorIndex(connector.index_name),
|
||||
}
|
||||
: undefined
|
||||
);
|
||||
|
||||
const onSave = () => {
|
||||
if (selectedIndex?.shouldCreate) {
|
||||
createIndex({ indexName: selectedIndex.label, language: selectedLanguage ?? null });
|
||||
} else if (selectedIndex && !(selectedIndex.label === connector.index_name)) {
|
||||
attachIndex({ connectorId: connector.id, indexName: selectedIndex.label });
|
||||
if (!selectedIndex) return;
|
||||
// Always attach and/or create prefixed index for managed connectors
|
||||
const prefixedIndex = prefixConnectorIndex(selectedIndex.label);
|
||||
if (selectedIndex.shouldCreate) {
|
||||
createIndex({
|
||||
indexName: prefixedIndex,
|
||||
language: null,
|
||||
});
|
||||
} else if (connector.index_name !== prefixedIndex) {
|
||||
attachIndex({
|
||||
connectorId: connector.id,
|
||||
indexName: prefixedIndex,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// For managed connectors ensure that only prefixed indices are displayed in the dropdown
|
||||
// This takes care of the initial component state where all indices could be displayed briefly
|
||||
const options: Array<EuiComboBoxOptionOption<string>> = isLoading
|
||||
? []
|
||||
: data?.indexNames.map((name) => {
|
||||
return {
|
||||
: data?.indexNames
|
||||
.filter((name) => !connector.is_native || name.startsWith(MANAGED_CONNECTOR_INDEX_PREFIX))
|
||||
.map((name) => ({
|
||||
label: name,
|
||||
};
|
||||
}) ?? [];
|
||||
value: removePrefixConnectorIndex(name),
|
||||
})) ?? [];
|
||||
|
||||
const hasMatchingOptions =
|
||||
data?.indexNames.some((name) =>
|
||||
name.toLocaleLowerCase().includes(query?.searchValue.toLocaleLowerCase() ?? '')
|
||||
) ?? false;
|
||||
const isFullMatch =
|
||||
data?.indexNames.some(
|
||||
(name) => name.toLocaleLowerCase() === query?.searchValue.toLocaleLowerCase()
|
||||
name
|
||||
.toLocaleLowerCase()
|
||||
.includes(prefixConnectorIndex(query?.searchValue?.toLocaleLowerCase() || ''))
|
||||
) ?? false;
|
||||
|
||||
const shouldPrependUserInputAsOption = !!query?.searchValue && hasMatchingOptions && !isFullMatch;
|
||||
const isFullMatch =
|
||||
data?.indexNames.some(
|
||||
(name) =>
|
||||
name.toLocaleLowerCase() ===
|
||||
prefixConnectorIndex(query?.searchValue?.toLocaleLowerCase() || '')
|
||||
) ?? false;
|
||||
|
||||
const shouldPrependUserInputAsOption =
|
||||
!!query &&
|
||||
!!query.searchValue &&
|
||||
query.searchValue !== MANAGED_CONNECTOR_INDEX_PREFIX &&
|
||||
hasMatchingOptions &&
|
||||
!isFullMatch;
|
||||
|
||||
const groupedOptions: Array<EuiComboBoxOptionOption<string>> = shouldPrependUserInputAsOption
|
||||
? [
|
||||
...[
|
||||
{
|
||||
label: CREATE_NEW_INDEX_GROUP_LABEL,
|
||||
options: [
|
||||
{
|
||||
label: query.searchValue,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
...[{ label: SELECT_EXISTING_INDEX_GROUP_LABEL, options }],
|
||||
{
|
||||
label: CREATE_NEW_INDEX_GROUP_LABEL,
|
||||
options: [
|
||||
{
|
||||
label: prefixConnectorIndex(query!.searchValue),
|
||||
value: query!.searchValue,
|
||||
},
|
||||
],
|
||||
},
|
||||
{ label: SELECT_EXISTING_INDEX_GROUP_LABEL, options },
|
||||
]
|
||||
: [{ label: SELECT_EXISTING_INDEX_GROUP_LABEL, options }];
|
||||
|
||||
|
@ -144,7 +192,8 @@ export const AttachIndexBox: React.FC<AttachIndexBoxProps> = ({ connector }) =>
|
|||
}, [query]);
|
||||
|
||||
useEffect(() => {
|
||||
setSanitizedName(formatApiName(connector.name));
|
||||
// Suggested name for managed connector should include the content- prefix
|
||||
setSanitizedName(prefixConnectorIndex(formatApiName(connector.name)));
|
||||
}, [connector.name]);
|
||||
|
||||
const { hash } = useLocation();
|
||||
|
@ -170,9 +219,10 @@ export const AttachIndexBox: React.FC<AttachIndexBoxProps> = ({ connector }) =>
|
|||
}
|
||||
)
|
||||
: attachApiError?.body?.message || createApiError?.body?.message || undefined;
|
||||
|
||||
if (indexName) {
|
||||
// We don't want to let people edit indices when on the index route
|
||||
return <></>;
|
||||
// Do not render when on the index route
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
|
@ -189,8 +239,8 @@ export const AttachIndexBox: React.FC<AttachIndexBoxProps> = ({ connector }) =>
|
|||
<FormattedMessage
|
||||
id="xpack.enterpriseSearch.attachIndexBox.thisIndexWillHoldTextLabel"
|
||||
defaultMessage="This index will hold your data source content, and is optimized with default field mappings
|
||||
for relevant search experiences. Give your index a unique name and optionally set a default
|
||||
language analyzer for the index."
|
||||
for relevant search experiences. Give your index a unique name and optionally set a default
|
||||
language analyzer for the index."
|
||||
/>
|
||||
</EuiText>
|
||||
<EuiSpacer />
|
||||
|
@ -201,10 +251,20 @@ export const AttachIndexBox: React.FC<AttachIndexBoxProps> = ({ connector }) =>
|
|||
'xpack.enterpriseSearch.attachIndexBox.euiFormRow.associatedIndexLabel',
|
||||
{ defaultMessage: 'Associated index' }
|
||||
)}
|
||||
helpText={i18n.translate(
|
||||
'xpack.enterpriseSearch.attachIndexBox.euiFormRow.associatedIndexHelpTextLabel',
|
||||
{ defaultMessage: 'You can use an existing index or create a new one.' }
|
||||
)}
|
||||
helpText={
|
||||
connector.is_native
|
||||
? i18n.translate(
|
||||
'xpack.enterpriseSearch.attachIndexBox.euiFormRow.associatedManagedConnectorIndexHelpTextLabel',
|
||||
{
|
||||
defaultMessage:
|
||||
'Managed connector indices must be prefixed. Use an existing index or create a new one.',
|
||||
}
|
||||
)
|
||||
: i18n.translate(
|
||||
'xpack.enterpriseSearch.attachIndexBox.euiFormRow.associatedIndexHelpTextLabel',
|
||||
{ defaultMessage: 'You can use an existing index or create a new one.' }
|
||||
)
|
||||
}
|
||||
error={error}
|
||||
isInvalid={!!error}
|
||||
>
|
||||
|
@ -217,11 +277,13 @@ export const AttachIndexBox: React.FC<AttachIndexBoxProps> = ({ connector }) =>
|
|||
'xpack.enterpriseSearch.attachIndexBox.euiFormRow.indexSelector.customOption',
|
||||
{
|
||||
defaultMessage: 'Create index {searchValue}',
|
||||
values: { searchValue: '{searchValue}' },
|
||||
values: { searchValue: prefixConnectorIndex('{searchValue}') },
|
||||
}
|
||||
)}
|
||||
isLoading={isLoading}
|
||||
options={groupedOptions}
|
||||
singleSelection={{ asPlainText: connector.is_native }}
|
||||
prepend={connector.is_native ? MANAGED_CONNECTOR_INDEX_PREFIX : undefined}
|
||||
onKeyDown={(event) => {
|
||||
// Index name should not contain spaces
|
||||
if (event.key === ' ') {
|
||||
|
@ -229,28 +291,34 @@ export const AttachIndexBox: React.FC<AttachIndexBoxProps> = ({ connector }) =>
|
|||
}
|
||||
}}
|
||||
onSearchChange={(searchValue) => {
|
||||
// Match by option value to ensure accurate comparison with non-prefixed
|
||||
// user input for managed connectors
|
||||
setQuery({
|
||||
isFullMatch: options.some((option) => option.label === searchValue),
|
||||
searchValue,
|
||||
isFullMatch: options.some(
|
||||
(option) => option.value === prefixConnectorIndex(searchValue)
|
||||
),
|
||||
searchValue: prefixConnectorIndex(searchValue),
|
||||
});
|
||||
}}
|
||||
onChange={(selection) => {
|
||||
const currentSelection = selection[0] ?? undefined;
|
||||
const currentSelection = selection[0];
|
||||
const selectedIndexOption = currentSelection
|
||||
? {
|
||||
label: currentSelection.label,
|
||||
label: removePrefixConnectorIndex(currentSelection.label),
|
||||
shouldCreate:
|
||||
shouldPrependUserInputAsOption &&
|
||||
!!(currentSelection?.label === query?.searchValue),
|
||||
currentSelection.value === query?.searchValue,
|
||||
}
|
||||
: undefined;
|
||||
setSelectedIndex(selectedIndexOption);
|
||||
}}
|
||||
selectedOptions={selectedIndex ? [selectedIndex] : undefined}
|
||||
onCreateOption={(value) => {
|
||||
setSelectedIndex({ label: value.trim(), shouldCreate: true });
|
||||
setSelectedIndex({
|
||||
label: removePrefixConnectorIndex(value.trim()),
|
||||
shouldCreate: true,
|
||||
});
|
||||
}}
|
||||
singleSelection
|
||||
/>
|
||||
</EuiFormRow>
|
||||
</EuiFlexItem>
|
||||
|
@ -261,8 +329,12 @@ export const AttachIndexBox: React.FC<AttachIndexBoxProps> = ({ connector }) =>
|
|||
<EuiButton
|
||||
data-test-subj="entSearchContent-connector-connectorDetail-saveConfigurationButton"
|
||||
data-telemetry-id="entSearchContent-connector-connectorDetail-saveConfigurationButton"
|
||||
onClick={() => onSave()}
|
||||
disabled={!selectedIndex || selectedIndex.label === connector.index_name}
|
||||
onClick={onSave}
|
||||
disabled={
|
||||
!selectedIndex ||
|
||||
prefixConnectorIndex(selectedIndex.label) === connector.index_name ||
|
||||
!!error
|
||||
}
|
||||
isLoading={isSaveLoading}
|
||||
>
|
||||
{i18n.translate('xpack.enterpriseSearch.attachIndexBox.saveConfigurationButtonLabel', {
|
||||
|
@ -314,15 +386,13 @@ export const AttachIndexBox: React.FC<AttachIndexBoxProps> = ({ connector }) =>
|
|||
}
|
||||
)}
|
||||
</EuiButton>
|
||||
{indexExists[sanitizedName] ? (
|
||||
{indexExists[sanitizedName] && (
|
||||
<EuiText size="xs">
|
||||
{i18n.translate('xpack.enterpriseSearch.attachIndexBox.indexNameExistsError', {
|
||||
defaultMessage: 'Index with name {indexName} already exists',
|
||||
values: { indexName: sanitizedName },
|
||||
})}
|
||||
</EuiText>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
|
|
|
@ -26,9 +26,6 @@ import { BetaConnectorCallout } from '../../../shared/beta/beta_connector_callou
|
|||
import { HttpLogic } from '../../../shared/http';
|
||||
import { KibanaLogic } from '../../../shared/kibana';
|
||||
|
||||
import { GenerateConnectorApiKeyApiLogic } from '../../api/connector/generate_connector_api_key_api_logic';
|
||||
|
||||
import { ApiKeyConfig } from '../search_index/connector/api_key_configuration';
|
||||
import { ConvertConnector } from '../search_index/connector/native_connector_configuration/convert_connector';
|
||||
import { NativeConnectorConfigurationConfig } from '../search_index/connector/native_connector_configuration/native_connector_configuration_config';
|
||||
import { ResearchConfiguration } from '../search_index/connector/native_connector_configuration/research_configuration';
|
||||
|
@ -41,7 +38,6 @@ export const NativeConnectorConfiguration: React.FC = () => {
|
|||
const { connector } = useValues(ConnectorViewLogic);
|
||||
const { config, connectorTypes: connectors } = useValues(KibanaLogic);
|
||||
const { errorConnectingMessage } = useValues(HttpLogic);
|
||||
const { data: apiKeyData } = useValues(GenerateConnectorApiKeyApiLogic);
|
||||
|
||||
const NATIVE_CONNECTORS = useMemo(
|
||||
() => connectors.filter(({ isNative }) => isNative),
|
||||
|
@ -68,7 +64,6 @@ export const NativeConnectorConfiguration: React.FC = () => {
|
|||
};
|
||||
|
||||
const iconPath = nativeConnector.iconPath;
|
||||
const hasApiKey = !!(connector.api_key_id ?? apiKeyData);
|
||||
|
||||
// TODO service_type === "" is considered unknown/custom connector multipleplaces replace all of them with a better solution
|
||||
const isBeta =
|
||||
|
@ -170,23 +165,6 @@ export const NativeConnectorConfiguration: React.FC = () => {
|
|||
<EuiSpacer />
|
||||
</EuiPanel>
|
||||
<EuiSpacer />
|
||||
<EuiPanel hasBorder>
|
||||
<EuiTitle size="s">
|
||||
<h4>
|
||||
{i18n.translate(
|
||||
'xpack.enterpriseSearch.content.connector_detail.nativeConfigurationConnector.apiKey.title',
|
||||
{ defaultMessage: 'API Key' }
|
||||
)}
|
||||
</h4>
|
||||
</EuiTitle>
|
||||
<EuiSpacer size="m" />
|
||||
<ApiKeyConfig
|
||||
indexName={connector.index_name || ''}
|
||||
hasApiKey={hasApiKey}
|
||||
isNative
|
||||
/>
|
||||
</EuiPanel>
|
||||
<EuiSpacer />
|
||||
<EuiPanel hasBorder>
|
||||
<ConvertConnector />
|
||||
</EuiPanel>
|
||||
|
|
|
@ -95,7 +95,7 @@ export const ConnectorDetailOverview: React.FC = () => {
|
|||
<>
|
||||
<EuiCallOut
|
||||
iconType="iInCircle"
|
||||
color="danger"
|
||||
color="warning"
|
||||
title={i18n.translate(
|
||||
'xpack.enterpriseSearch.content.connectors.overview.connectorNoIndexCallOut.title',
|
||||
{
|
||||
|
@ -115,7 +115,7 @@ export const ConnectorDetailOverview: React.FC = () => {
|
|||
</EuiText>
|
||||
<EuiSpacer />
|
||||
<EuiButtonTo
|
||||
color="danger"
|
||||
color="warning"
|
||||
fill
|
||||
to={`${generateEncodedPath(CONNECTOR_DETAIL_TAB_PATH, {
|
||||
connectorId: connector.id,
|
||||
|
|
|
@ -144,6 +144,7 @@ export const StartStep: React.FC<StartStepProps> = ({
|
|||
generateConnectorName({
|
||||
connectorName: rawName,
|
||||
connectorType: selectedConnector.serviceType,
|
||||
isManagedConnector: selectedConnector.isNative,
|
||||
});
|
||||
}
|
||||
}}
|
||||
|
|
|
@ -192,6 +192,7 @@ export const NewConnectorLogic = kea<MakeLogicType<NewConnectorValues, NewConnec
|
|||
if (connector) {
|
||||
actions.generateConnectorName({
|
||||
connectorType: connector.serviceType,
|
||||
isManagedConnector: connector.isNative,
|
||||
});
|
||||
}
|
||||
},
|
||||
|
|
|
@ -7,7 +7,11 @@
|
|||
|
||||
import { IScopedClusterClient } from '@kbn/core/server';
|
||||
|
||||
import { Connector, CONNECTORS_INDEX } from '@kbn/search-connectors';
|
||||
import {
|
||||
Connector,
|
||||
CONNECTORS_INDEX,
|
||||
MANAGED_CONNECTOR_INDEX_PREFIX,
|
||||
} from '@kbn/search-connectors';
|
||||
|
||||
import { createIndex } from '../indices/create_index';
|
||||
import { indexOrAliasExists } from '../indices/exists_index';
|
||||
|
@ -20,10 +24,10 @@ export const generateConfig = async (client: IScopedClusterClient, connector: Co
|
|||
if (connector.index_name) {
|
||||
associatedIndex = connector.index_name;
|
||||
} else {
|
||||
associatedIndex = await generatedIndexName(
|
||||
client,
|
||||
connector.name || connector.service_type || 'my-connector' // pass a default name to generate a readable index name rather than gibberish
|
||||
);
|
||||
const indexPrefix = connector.is_native ? MANAGED_CONNECTOR_INDEX_PREFIX : ''; // managed connectors need to be prefixed with `content-`
|
||||
const connectorReference = connector.name || connector.service_type || 'my-connector'; // pass a default name to generate a readable index name rather than gibberish
|
||||
|
||||
associatedIndex = await generatedIndexName(client, indexPrefix + connectorReference);
|
||||
}
|
||||
|
||||
if (!indexOrAliasExists(client, associatedIndex)) {
|
||||
|
|
|
@ -9,22 +9,34 @@ import { v4 as uuidv4 } from 'uuid';
|
|||
|
||||
import { IScopedClusterClient } from '@kbn/core-elasticsearch-server';
|
||||
|
||||
import { MANAGED_CONNECTOR_INDEX_PREFIX } from '@kbn/search-connectors';
|
||||
|
||||
import { ErrorCode } from '../../../common/types/error_codes';
|
||||
|
||||
import { toAlphanumeric } from '../../../common/utils/to_alphanumeric';
|
||||
import { indexOrAliasExists } from '../indices/exists_index';
|
||||
|
||||
const addIndexPrefix = (indexName: string, isManagedConnector: boolean): string => {
|
||||
const indexPrefix = isManagedConnector ? MANAGED_CONNECTOR_INDEX_PREFIX : 'connector-';
|
||||
return `${indexPrefix}${indexName}`;
|
||||
};
|
||||
|
||||
const addConnectorPrefix = (indexName: string): string => {
|
||||
return `connector-${indexName}`;
|
||||
};
|
||||
|
||||
export const generateConnectorName = async (
|
||||
client: IScopedClusterClient,
|
||||
connectorType: string,
|
||||
userConnectorName?: string
|
||||
userConnectorName?: string,
|
||||
isManagedConnector: boolean = false
|
||||
): Promise<{ apiKeyName: string; connectorName: string; indexName: string }> => {
|
||||
const prefix = toAlphanumeric(connectorType);
|
||||
if (!prefix || prefix.length === 0) {
|
||||
throw new Error('Connector type or connectorName is required');
|
||||
}
|
||||
if (userConnectorName) {
|
||||
let indexName = `connector-${userConnectorName}`;
|
||||
let indexName = addIndexPrefix(userConnectorName, isManagedConnector);
|
||||
const resultSameName = await indexOrAliasExists(client, indexName);
|
||||
// index with same name doesn't exist
|
||||
if (!resultSameName) {
|
||||
|
@ -36,12 +48,14 @@ export const generateConnectorName = async (
|
|||
}
|
||||
// if the index name already exists, we will generate until it doesn't for 20 times
|
||||
for (let i = 0; i < 20; i++) {
|
||||
indexName = `connector-${userConnectorName}-${uuidv4().split('-')[1].slice(0, 4)}`;
|
||||
const randomizedConnectorName = `${userConnectorName}-${uuidv4().split('-')[1].slice(0, 4)}`;
|
||||
|
||||
indexName = addIndexPrefix(randomizedConnectorName, isManagedConnector);
|
||||
|
||||
const result = await indexOrAliasExists(client, indexName);
|
||||
if (!result) {
|
||||
return {
|
||||
apiKeyName: indexName,
|
||||
apiKeyName: addConnectorPrefix(randomizedConnectorName),
|
||||
connectorName: userConnectorName,
|
||||
indexName,
|
||||
};
|
||||
|
@ -49,14 +63,15 @@ export const generateConnectorName = async (
|
|||
}
|
||||
} else {
|
||||
for (let i = 0; i < 20; i++) {
|
||||
const connectorName = `${prefix}-${uuidv4().split('-')[1].slice(0, 4)}`;
|
||||
const indexName = `connector-${connectorName}`;
|
||||
const randomizedConnectorName = `${prefix}-${uuidv4().split('-')[1].slice(0, 4)}`;
|
||||
const indexName = addIndexPrefix(randomizedConnectorName, isManagedConnector);
|
||||
|
||||
const result = await indexOrAliasExists(client, indexName);
|
||||
|
||||
if (!result) {
|
||||
return {
|
||||
apiKeyName: indexName,
|
||||
connectorName,
|
||||
apiKeyName: addConnectorPrefix(randomizedConnectorName),
|
||||
connectorName: randomizedConnectorName,
|
||||
indexName,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -842,17 +842,19 @@ export function registerConnectorRoutes({ router, log }: RouteDependencies) {
|
|||
body: schema.object({
|
||||
connectorName: schema.maybe(schema.string()),
|
||||
connectorType: schema.string(),
|
||||
isManagedConnector: schema.maybe(schema.boolean()),
|
||||
}),
|
||||
},
|
||||
},
|
||||
elasticsearchErrorHandler(log, async (context, request, response) => {
|
||||
const { client } = (await context.core).elasticsearch;
|
||||
const { connectorType, connectorName } = request.body;
|
||||
const { connectorType, connectorName, isManagedConnector } = request.body;
|
||||
try {
|
||||
const generatedNames = await generateConnectorName(
|
||||
client,
|
||||
connectorType ?? 'custom',
|
||||
connectorName
|
||||
connectorName,
|
||||
isManagedConnector
|
||||
);
|
||||
return response.ok({
|
||||
body: generatedNames,
|
||||
|
|
|
@ -17436,7 +17436,6 @@
|
|||
"xpack.enterpriseSearch.content.connector_detail.configurationConnector.steps.waitingForConnector.callout.finishLaterButton.label": "Terminer le déploiement plus tard",
|
||||
"xpack.enterpriseSearch.content.connector_detail.configurationConnector.steps.waitingForConnector.callout.title": "En attente de votre connecteur",
|
||||
"xpack.enterpriseSearch.content.connector_detail.configurationConnector.steps.waitingForConnector.title": "En attente du contrôle de votre connecteur",
|
||||
"xpack.enterpriseSearch.content.connector_detail.nativeConfigurationConnector.apiKey.title": "Clé d'API",
|
||||
"xpack.enterpriseSearch.content.connector_detail.nativeConfigurationConnector.configuration.title": "Configuration",
|
||||
"xpack.enterpriseSearch.content.connectors.breadcrumb": "Connecteurs",
|
||||
"xpack.enterpriseSearch.content.connectors.connectorDetail.configurationTabLabel": "Configuration",
|
||||
|
|
|
@ -17411,7 +17411,6 @@
|
|||
"xpack.enterpriseSearch.content.connector_detail.configurationConnector.steps.waitingForConnector.callout.finishLaterButton.label": "後でデプロイを完了する",
|
||||
"xpack.enterpriseSearch.content.connector_detail.configurationConnector.steps.waitingForConnector.callout.title": "コネクターを待機しています",
|
||||
"xpack.enterpriseSearch.content.connector_detail.configurationConnector.steps.waitingForConnector.title": "コネクターのチェックインを待機しています",
|
||||
"xpack.enterpriseSearch.content.connector_detail.nativeConfigurationConnector.apiKey.title": "API キー",
|
||||
"xpack.enterpriseSearch.content.connector_detail.nativeConfigurationConnector.configuration.title": "構成",
|
||||
"xpack.enterpriseSearch.content.connectors.breadcrumb": "コネクター",
|
||||
"xpack.enterpriseSearch.content.connectors.connectorDetail.configurationTabLabel": "構成",
|
||||
|
|
|
@ -17079,7 +17079,6 @@
|
|||
"xpack.enterpriseSearch.content.connector_detail.configurationConnector.steps.waitingForConnector.callout.finishLaterButton.label": "稍后完成部署",
|
||||
"xpack.enterpriseSearch.content.connector_detail.configurationConnector.steps.waitingForConnector.callout.title": "等候您的连接器",
|
||||
"xpack.enterpriseSearch.content.connector_detail.configurationConnector.steps.waitingForConnector.title": "等待您的连接器签入",
|
||||
"xpack.enterpriseSearch.content.connector_detail.nativeConfigurationConnector.apiKey.title": "API 密钥",
|
||||
"xpack.enterpriseSearch.content.connector_detail.nativeConfigurationConnector.configuration.title": "配置",
|
||||
"xpack.enterpriseSearch.content.connectors.breadcrumb": "连接器",
|
||||
"xpack.enterpriseSearch.content.connectors.connectorDetail.configurationTabLabel": "配置",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue