[Workplace Search] Update UX for custom api source creation flow (#127155)

This commit is contained in:
Byron Hulcher 2022-03-22 15:26:29 -04:00 committed by GitHub
parent 22e481af6b
commit 12e7894018
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
23 changed files with 542 additions and 570 deletions

View file

@ -139,6 +139,7 @@ export const getDocLinks = ({ kibanaBranch }: GetDocLinkOptions): DocLinks => {
security: `${WORKPLACE_SEARCH_DOCS}workplace-search-security.html`,
serviceNow: `${WORKPLACE_SEARCH_DOCS}workplace-search-servicenow-connector.html`,
sharePoint: `${WORKPLACE_SEARCH_DOCS}workplace-search-sharepoint-online-connector.html`,
sharePointServer: `${WORKPLACE_SEARCH_DOCS}sharepoint-server.html`,
slack: `${WORKPLACE_SEARCH_DOCS}workplace-search-slack-connector.html`,
synch: `${WORKPLACE_SEARCH_DOCS}workplace-search-customizing-indexing-rules.html`,
zendesk: `${WORKPLACE_SEARCH_DOCS}workplace-search-zendesk-connector.html`,

View file

@ -128,6 +128,7 @@ export interface DocLinks {
readonly security: string;
readonly serviceNow: string;
readonly sharePoint: string;
readonly sharePointServer: string;
readonly slack: string;
readonly synch: string;
readonly zendesk: string;

View file

@ -59,6 +59,7 @@ class DocLinks {
public workplaceSearchSecurity: string;
public workplaceSearchServiceNow: string;
public workplaceSearchSharePoint: string;
public workplaceSearchSharePointServer: string;
public workplaceSearchSlack: string;
public workplaceSearchSynch: string;
public workplaceSearchZendesk: string;
@ -115,6 +116,7 @@ class DocLinks {
this.workplaceSearchSecurity = '';
this.workplaceSearchServiceNow = '';
this.workplaceSearchSharePoint = '';
this.workplaceSearchSharePointServer = '';
this.workplaceSearchSlack = '';
this.workplaceSearchSynch = '';
this.workplaceSearchZendesk = '';
@ -174,6 +176,7 @@ class DocLinks {
this.workplaceSearchSecurity = docLinks.links.workplaceSearch.security;
this.workplaceSearchServiceNow = docLinks.links.workplaceSearch.serviceNow;
this.workplaceSearchSharePoint = docLinks.links.workplaceSearch.sharePoint;
this.workplaceSearchSharePointServer = docLinks.links.workplaceSearch.sharePointServer;
this.workplaceSearchSlack = docLinks.links.workplaceSearch.slack;
this.workplaceSearchSynch = docLinks.links.workplaceSearch.synch;
this.workplaceSearchZendesk = docLinks.links.workplaceSearch.zendesk;

View file

@ -66,7 +66,6 @@ export interface Configuration {
needsConfiguration?: boolean;
hasOauthRedirect: boolean;
baseUrlTitle?: string;
helpText?: string;
documentationUrl: string;
applicationPortalUrl?: string;
applicationLinkTitle?: string;

View file

@ -5,9 +5,9 @@
* 2.0.
*/
import '../../../../../__mocks__/shallow_useeffect.mock';
import { setMockValues } from '../../../../../__mocks__/kea_logic';
import { sourceConfigData } from '../../../../__mocks__/content_sources.mock';
import '../../../../../../__mocks__/shallow_useeffect.mock';
import { setMockValues } from '../../../../../../__mocks__/kea_logic';
import { sourceConfigData } from '../../../../../__mocks__/content_sources.mock';
import React from 'react';
@ -16,8 +16,8 @@ import { shallow } from 'enzyme';
import {
WorkplaceSearchPageTemplate,
PersonalDashboardLayout,
} from '../../../../components/layout';
import { staticSourceData } from '../../source_data';
} from '../../../../../components/layout';
import { staticSourceData } from '../../../source_data';
import { AddCustomSource } from './add_custom_source';
import { AddCustomSourceSteps } from './add_custom_source_logic';

View file

@ -9,21 +9,19 @@ import React from 'react';
import { useValues } from 'kea';
import { AppLogic } from '../../../../app_logic';
import { AppLogic } from '../../../../../app_logic';
import {
WorkplaceSearchPageTemplate,
PersonalDashboardLayout,
} from '../../../../components/layout';
import { NAV } from '../../../../constants';
} from '../../../../../components/layout';
import { NAV } from '../../../../../constants';
import { SourceDataItem } from '../../../../types';
import { SourceDataItem } from '../../../../../types';
import { AddCustomSourceLogic, AddCustomSourceSteps } from './add_custom_source_logic';
import { ConfigureCustom } from './configure_custom';
import { SaveCustom } from './save_custom';
import './add_source.scss';
interface Props {
sourceData: SourceDataItem;
initialValue?: string;

View file

@ -9,22 +9,21 @@ import {
LogicMounter,
mockFlashMessageHelpers,
mockHttpValues,
} from '../../../../../__mocks__/kea_logic';
import { sourceConfigData } from '../../../../__mocks__/content_sources.mock';
} from '../../../../../../__mocks__/kea_logic';
import { sourceConfigData } from '../../../../../__mocks__/content_sources.mock';
import { i18n } from '@kbn/i18n';
import { nextTick } from '@kbn/test-jest-helpers';
import { docLinks } from '../../../../../shared/doc_links';
import { itShowsServerErrorAsFlashMessage } from '../../../../../test_helpers';
import { docLinks } from '../../../../../../shared/doc_links';
import { itShowsServerErrorAsFlashMessage } from '../../../../../../test_helpers';
jest.mock('../../../../app_logic', () => ({
jest.mock('../../../../../app_logic', () => ({
AppLogic: { values: { isOrganization: true } },
}));
import { AppLogic } from '../../../../app_logic';
import { AppLogic } from '../../../../../app_logic';
import { SOURCE_NAMES } from '../../../../constants';
import { CustomSource, SourceDataItem } from '../../../../types';
import { SOURCE_NAMES } from '../../../../../constants';
import { CustomSource, SourceDataItem } from '../../../../../types';
import { AddCustomSourceLogic, AddCustomSourceSteps } from './add_custom_source_logic';
@ -36,10 +35,6 @@ const CUSTOM_SOURCE_DATA_ITEM: SourceDataItem = {
isPublicKey: false,
hasOauthRedirect: false,
needsBaseUrl: false,
helpText: i18n.translate('xpack.enterpriseSearch.workplaceSearch.sources.helpText.custom', {
defaultMessage:
'To create a Custom API Source, provide a human-readable and descriptive name. The name will appear as-is in the various search experiences and management interfaces.',
}),
documentationUrl: docLinks.workplaceSearchCustomSources,
applicationPortalUrl: '',
},

View file

@ -7,10 +7,10 @@
import { kea, MakeLogicType } from 'kea';
import { flashAPIErrors, clearFlashMessages } from '../../../../../shared/flash_messages';
import { HttpLogic } from '../../../../../shared/http';
import { AppLogic } from '../../../../app_logic';
import { CustomSource, SourceDataItem } from '../../../../types';
import { flashAPIErrors, clearFlashMessages } from '../../../../../../shared/flash_messages';
import { HttpLogic } from '../../../../../../shared/http';
import { AppLogic } from '../../../../../app_logic';
import { CustomSource, SourceDataItem } from '../../../../../types';
export interface AddCustomSourceProps {
sourceData: SourceDataItem;

View file

@ -5,8 +5,8 @@
* 2.0.
*/
import '../../../../../__mocks__/shallow_useeffect.mock';
import { setMockActions, setMockValues } from '../../../../../__mocks__/kea_logic';
import '../../../../../../__mocks__/shallow_useeffect.mock';
import { setMockActions, setMockValues } from '../../../../../../__mocks__/kea_logic';
import React from 'react';
@ -14,7 +14,7 @@ import { shallow } from 'enzyme';
import { EuiForm, EuiFieldText } from '@elastic/eui';
import { staticSourceData } from '../../source_data';
import { staticSourceData } from '../../../source_data';
import { ConfigureCustom } from './configure_custom';
@ -50,7 +50,7 @@ describe('ConfigureCustom', () => {
const wrapper = shallow(<ConfigureCustom />);
const preventDefault = jest.fn();
wrapper.find('form').simulate('submit', { preventDefault });
wrapper.find('EuiForm').simulate('submit', { preventDefault });
expect(preventDefault).toHaveBeenCalled();
expect(createContentSource).toHaveBeenCalled();

View file

@ -0,0 +1,202 @@
/*
* 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, { ChangeEvent, FormEvent } from 'react';
import { useActions, useValues } from 'kea';
import {
EuiButton,
EuiFieldText,
EuiFlexGroup,
EuiFlexItem,
EuiForm,
EuiFormRow,
EuiLink,
EuiSpacer,
EuiText,
EuiTitle,
} from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n-react';
import { docLinks } from '../../../../../../shared/doc_links';
import connectionIllustration from '../../../../../assets/connection_illustration.svg';
import { SOURCE_NAME_LABEL } from '../../../constants';
import { AddSourceHeader } from '../add_source_header';
import { CONFIG_CUSTOM_BUTTON, CONFIG_CUSTOM_LINK_TEXT, CONFIG_INTRO_ALT_TEXT } from '../constants';
import { AddCustomSourceLogic } from './add_custom_source_logic';
export const ConfigureCustom: React.FC = () => {
const { setCustomSourceNameValue, createContentSource } = useActions(AddCustomSourceLogic);
const { customSourceNameValue, buttonLoading, sourceData } = useValues(AddCustomSourceLogic);
const handleFormSubmit = (e: FormEvent) => {
e.preventDefault();
createContentSource();
};
const handleNameChange = (e: ChangeEvent<HTMLInputElement>) =>
setCustomSourceNameValue(e.target.value);
const {
serviceType,
configuration: { documentationUrl, githubRepository },
name,
categories = [],
} = sourceData;
return (
<>
<AddSourceHeader name={name} serviceType={serviceType} categories={categories} />
<EuiSpacer size="xxl" />
<EuiFlexGroup
justifyContent="flexStart"
alignItems="stretch"
direction="row"
gutterSize="xl"
responsive={false}
>
<EuiFlexItem grow={false}>
<div className="adding-a-source__intro-image">
<img src={connectionIllustration} alt={CONFIG_INTRO_ALT_TEXT} />
</div>
</EuiFlexItem>
<EuiFlexItem>
<EuiSpacer size="xl" />
<EuiTitle size="l">
<h2>
<FormattedMessage
id="xpack.enterpriseSearch.workplaceSearch.contentSource.configCustom.title"
defaultMessage="How to add {name}"
values={{ name }}
/>
</h2>
</EuiTitle>
<EuiSpacer size="m" />
<EuiForm component="form" onSubmit={handleFormSubmit}>
<EuiText grow={false}>
{serviceType === 'custom' ? (
<>
<p>
<FormattedMessage
id="xpack.enterpriseSearch.workplaceSearch.sources.helpText.custom"
defaultMessage="To create a Custom API Source, provide a human-readable and descriptive name. The name will appear as-is in the various search experiences and management interfaces."
/>
</p>
<p>
<FormattedMessage
id="xpack.enterpriseSearch.workplaceSearch.contentSource.configCustom.docs.link.description"
defaultMessage="{link} to learn more about Custom API Sources."
values={{
link: (
<EuiLink href={docLinks.workplaceSearchCustomSources} target="_blank">
{CONFIG_CUSTOM_LINK_TEXT}
</EuiLink>
),
}}
/>
</p>
</>
) : (
<>
<p>
<FormattedMessage
id="xpack.enterpriseSearch.workplaceSearch.contentSource.configCustom.deploymentGuide.heading"
defaultMessage="The {name} connector is fully customizable, and will be self-managed on the infrastructure of your choice."
values={{
name,
}}
/>
</p>
<p>
<FormattedMessage
id="xpack.enterpriseSearch.workplaceSearch.contentSource.configCustom.deploymentGuide.description"
defaultMessage="To be prepared for configuration, review our {deploymentGuideLink} for all prerequisites needed to quickly deploy the connector package. Finalize your configuration in Enterprise Search with a descriptive name for the {name} content source, and update the connector config file with the source ID provided in the next step."
values={{
name,
deploymentGuideLink: (
<EuiLink target="_blank" href={documentationUrl}>
<FormattedMessage
id="xpack.enterpriseSearch.workplaceSearch.contentSource.configCustom.deploymentGuide.linkLabel"
defaultMessage="documentation"
/>
</EuiLink>
),
}}
/>
</p>
<p>
<EuiLink target="_blank" href={`https://github.com/${githubRepository}`}>
<FormattedMessage
id="xpack.enterpriseSearch.workplaceSearch.contentSource.configCustom.deploymentGuide.githubRepoLinkLabel"
defaultMessage="Customize the connector here."
/>
</EuiLink>
</p>
<p>
<EuiLink
target="_blank"
href={'https://discuss.elastic.co/c/enterprise-search/84'}
>
<FormattedMessage
id="xpack.enterpriseSearch.workplaceSearch.contentSource.configCustom.deploymentGuide.discussLinkLabel"
defaultMessage="Questions? Discuss here."
/>
</EuiLink>
</p>
<p>
<EuiLink target="_blank" href={'https://www.elastic.co/kibana/feedback'}>
<FormattedMessage
id="xpack.enterpriseSearch.workplaceSearch.contentSource.configCustom.deploymentGuide.feedbackLinkLabel"
defaultMessage="We're always looking to improve. Share your feedback "
/>
</EuiLink>
</p>
</>
)}
</EuiText>
<EuiSpacer size="xxl" />
<EuiFormRow label={SOURCE_NAME_LABEL}>
<EuiFieldText
name="source-name"
required
data-test-subj="CustomSourceNameInput"
value={customSourceNameValue}
onChange={handleNameChange}
/>
</EuiFormRow>
<EuiSpacer />
<EuiFormRow>
<EuiButton
color="primary"
fill
type="submit"
isLoading={buttonLoading}
data-test-subj="CreateCustomButton"
>
{serviceType === 'custom' ? (
CONFIG_CUSTOM_BUTTON
) : (
<FormattedMessage
id="xpack.enterpriseSearch.workplaceSearch.contentSource.configCustom.createNamedSourceButtonLabel"
defaultMessage="Configure {name}"
values={{
name,
}}
/>
)}
</EuiButton>
</EuiFormRow>
</EuiForm>
</EuiFlexItem>
</EuiFlexGroup>
</>
);
};

View file

@ -0,0 +1,8 @@
/*
* 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.
*/
export { AddCustomSource } from './add_custom_source';

View file

@ -0,0 +1,87 @@
/*
* 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 { setMockValues } from '../../../../../../__mocks__/kea_logic';
import React from 'react';
import { shallow, ShallowWrapper } from 'enzyme';
import { EuiButtonTo } from '../../../../../../shared/react_router_helpers';
import { staticCustomSourceData } from '../../../source_data';
import { SourceIdentifier } from '../../source_identifier';
import { SaveCustom } from './save_custom';
const mockValues = {
newCustomSource: {
id: 'id',
accessToken: 'token',
name: 'name',
},
sourceData: staticCustomSourceData,
};
describe('SaveCustom', () => {
describe('default behavior', () => {
let wrapper: ShallowWrapper;
beforeAll(() => {
jest.clearAllMocks();
setMockValues(mockValues);
wrapper = shallow(<SaveCustom />);
});
it('contains a button back to the sources list', () => {
expect(wrapper.find(EuiButtonTo)).toHaveLength(1);
});
it('contains a source identifier', () => {
expect(wrapper.find(SourceIdentifier)).toHaveLength(1);
});
it('includes a link to generic documentation', () => {
expect(wrapper.find('[data-test-subj="GenericDocumentationLink"]')).toHaveLength(1);
});
});
describe('for pre-configured custom sources', () => {
let wrapper: ShallowWrapper;
beforeAll(() => {
jest.clearAllMocks();
setMockValues({
...mockValues,
sourceData: {
...staticCustomSourceData,
serviceType: 'sharepoint-server',
configuration: {
...staticCustomSourceData.configuration,
githubRepository: 'elastic/sharepoint-server-connector',
},
},
});
wrapper = shallow(<SaveCustom />);
});
it('includes a to the github repository', () => {
expect(wrapper.find('[data-test-subj="GithubRepositoryLink"]')).toHaveLength(1);
});
it('includes a link to service-type specific documentation', () => {
expect(wrapper.find('[data-test-subj="PreconfiguredDocumentationLink"]')).toHaveLength(1);
});
it('includes a link to provide feedback', () => {
expect(wrapper.find('[data-test-subj="FeedbackCallout"]')).toHaveLength(1);
});
});
});

View file

@ -0,0 +1,201 @@
/*
* 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 { useValues } from 'kea';
import {
EuiFlexGroup,
EuiFlexItem,
EuiIcon,
EuiSpacer,
EuiText,
EuiTextAlign,
EuiTitle,
EuiLink,
EuiPanel,
EuiHorizontalRule,
EuiCallOut,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n-react';
import { EuiButtonTo, EuiLinkTo } from '../../../../../../shared/react_router_helpers';
import { AppLogic } from '../../../../../app_logic';
import { API_KEY_LABEL } from '../../../../../constants';
import { SOURCES_PATH, getSourcesPath, API_KEYS_PATH } from '../../../../../routes';
import { SourceIdentifier } from '../../source_identifier';
import { AddSourceHeader } from '../add_source_header';
import { SAVE_CUSTOM_BODY1 as READY_TO_ACCEPT_REQUESTS_LABEL } from '../constants';
import { AddCustomSourceLogic } from './add_custom_source_logic';
export const SaveCustom: React.FC = () => {
const { newCustomSource, sourceData } = useValues(AddCustomSourceLogic);
const { isOrganization } = useValues(AppLogic);
const {
serviceType,
configuration: { githubRepository, documentationUrl },
name,
categories = [],
} = sourceData;
return (
<>
<AddSourceHeader name={name} serviceType={serviceType} categories={categories} />
<EuiSpacer size="xxl" />
<EuiFlexGroup>
<EuiFlexItem>
<EuiPanel paddingSize="l" hasShadow={false} color="subdued">
<EuiFlexGroup
direction="column"
alignItems="center"
style={{ marginTop: 'auto', marginBottom: 'auto' }}
>
<EuiFlexItem>
<EuiIcon type="checkInCircleFilled" color="success" size="xxl" />
</EuiFlexItem>
<EuiFlexItem>
<EuiTitle size="l">
<EuiTextAlign textAlign="center">
<h2>
{i18n.translate(
'xpack.enterpriseSearch.workplaceSearch.contentSource.saveCustom.heading',
{
defaultMessage: '{name} Created',
values: { name: newCustomSource.name },
}
)}
</h2>
</EuiTextAlign>
</EuiTitle>
</EuiFlexItem>
<EuiFlexItem>
<EuiText grow={false}>
<EuiTextAlign textAlign="center">{READY_TO_ACCEPT_REQUESTS_LABEL}</EuiTextAlign>
</EuiText>
</EuiFlexItem>
<EuiFlexItem>
<EuiButtonTo
size="m"
color="primary"
fill
to={getSourcesPath(SOURCES_PATH, isOrganization)}
>
<FormattedMessage
id="xpack.enterpriseSearch.workplaceSearch.contentSource.saveCustom.configureNewSourceButtonLabel"
defaultMessage="Configure a new content source"
/>
</EuiButtonTo>
</EuiFlexItem>
</EuiFlexGroup>
</EuiPanel>
</EuiFlexItem>
<EuiFlexItem>
<EuiPanel paddingSize="l" hasShadow={false} color="subdued">
<EuiText>
{serviceType !== 'custom' && githubRepository ? (
<>
<FormattedMessage
data-test-subj="GithubRepositoryLink"
id="xpack.enterpriseSearch.workplaceSearch.contentSource.saveCustom.repositoryInstructions"
defaultMessage="Set up your connector by cloning the {githubRepositoryLink}"
values={{
githubRepositoryLink: (
<EuiLink target="_blank" href={`https://github.com/${githubRepository}`}>
<FormattedMessage
id="xpack.enterpriseSearch.workplaceSearch.contentSource.saveCustom.repositoryLinkLabel"
defaultMessage="{name} connector repository"
values={{ name }}
/>
</EuiLink>
),
}}
/>
<EuiSpacer size="s" />
<FormattedMessage
data-test-subj="PreconfiguredDocumentationLink"
id="xpack.enterpriseSearch.workplaceSearch.contentSource.saveCustom.deploymentInstructions"
defaultMessage="Review the {documentationLink} and deploy the connector package to be self managed on the infrastructure of your choice."
values={{
documentationLink: (
<EuiLink target="_blank" href={documentationUrl}>
<FormattedMessage
id="xpack.enterpriseSearch.workplaceSearch.contentSource.saveCustom.documentationLinkLabel"
defaultMessage="{name} connector documentation"
values={{ name }}
/>
</EuiLink>
),
}}
/>
</>
) : (
<FormattedMessage
data-test-subj="GenericDocumentationLink"
id="xpack.enterpriseSearch.workplaceSearch.contentSource.saveCustom.documentationHelpText"
defaultMessage="Review the {documentationLink} to learn how to build and deploy your own connector on the self managed infrastructure of your choice."
values={{
documentationLink: (
<EuiLink target="_blank" href={documentationUrl}>
<FormattedMessage
id="xpack.enterpriseSearch.workplaceSearch.contentSource.saveCustom.customAPISourceDocumentationLabel"
defaultMessage="Custom API source documentation"
/>
</EuiLink>
),
}}
/>
)}
<EuiSpacer size="s" />
<FormattedMessage
id="xpack.enterpriseSearch.workplaceSearch.sources.saveCustom.sourceIdentifierHelpText"
defaultMessage="Specify the following Source Identifier along with an {apiKeyLink} in the deployed connector's config file to sync documents."
values={{
apiKeyLink: (
<EuiLinkTo target="_blank" to={API_KEYS_PATH}>
{API_KEY_LABEL}
</EuiLinkTo>
),
}}
/>
</EuiText>
<EuiHorizontalRule />
<SourceIdentifier id={newCustomSource.id} />
</EuiPanel>
</EuiFlexItem>
</EuiFlexGroup>
{serviceType !== 'custom' && (
<>
<EuiSpacer />
<EuiFlexGroup justifyContent="center">
<EuiFlexItem grow={false}>
<EuiCallOut
data-test-subj="FeedbackCallout"
heading="h3"
size="s"
title={
<EuiLinkTo target="_blank" to={'https://www.elastic.co/kibana/feedback'}>
<FormattedMessage
id="xpack.enterpriseSearch.workplaceSearch.sources.feedbackLinkLabel"
defaultMessage="Have feedback about deploying a {name} connector? Let us know."
values={{ name }}
/>
</EuiLinkTo>
}
iconType="email"
/>
</EuiFlexItem>
</EuiFlexGroup>
</>
)}
</>
);
};

View file

@ -62,7 +62,7 @@ export const ConfigCompleted: React.FC<ConfigCompletedProps> = ({
<EuiFlexItem>
<EuiFlexGroup direction="column" alignItems="center" responsive={false}>
<EuiFlexItem>
<EuiIcon type="checkInCircleFilled" color="#42CC89" size="xxl" />
<EuiIcon type="checkInCircleFilled" color="success" size="xxl" />
</EuiFlexItem>
<EuiFlexItem>
<EuiText>

View file

@ -1,123 +0,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 React, { ChangeEvent, FormEvent } from 'react';
import { useActions, useValues } from 'kea';
import {
EuiButton,
EuiFieldText,
EuiForm,
EuiFormRow,
EuiLink,
EuiSpacer,
EuiText,
} from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n-react';
import { docLinks } from '../../../../../shared/doc_links';
import { SOURCE_NAME_LABEL } from '../../constants';
import { AddCustomSourceLogic } from './add_custom_source_logic';
import { AddSourceHeader } from './add_source_header';
import { CONFIG_CUSTOM_BUTTON, CONFIG_CUSTOM_LINK_TEXT } from './constants';
export const ConfigureCustom: React.FC = () => {
const { setCustomSourceNameValue, createContentSource } = useActions(AddCustomSourceLogic);
const { customSourceNameValue, buttonLoading, sourceData } = useValues(AddCustomSourceLogic);
const handleFormSubmit = (e: FormEvent) => {
e.preventDefault();
createContentSource();
};
const handleNameChange = (e: ChangeEvent<HTMLInputElement>) =>
setCustomSourceNameValue(e.target.value);
const {
serviceType,
configuration: { documentationUrl, helpText },
name,
categories = [],
} = sourceData;
return (
<>
<AddSourceHeader name={name} serviceType={serviceType} categories={categories} />
<EuiSpacer />
<form onSubmit={handleFormSubmit}>
<EuiForm>
<EuiText grow={false}>
<p>{helpText}</p>
<p>
{serviceType === 'custom' ? (
<FormattedMessage
id="xpack.enterpriseSearch.workplaceSearch.contentSource.configCustom.docs.link.description"
defaultMessage="{link} to learn more about Custom API Sources."
values={{
link: (
<EuiLink href={docLinks.workplaceSearchCustomSources} target="_blank">
{CONFIG_CUSTOM_LINK_TEXT}
</EuiLink>
),
}}
/>
) : (
<FormattedMessage
id="xpack.enterpriseSearch.workplaceSearch.contentSource.configCustom.deploymentDocs.link.description"
defaultMessage="{link} to learn more about deploying a {name} source."
values={{
link: (
<EuiLink target="_blank" href={documentationUrl}>
{CONFIG_CUSTOM_LINK_TEXT}
</EuiLink>
),
name,
}}
/>
)}
</p>
</EuiText>
<EuiSpacer size="xxl" />
<EuiFormRow label={SOURCE_NAME_LABEL}>
<EuiFieldText
name="source-name"
required
data-test-subj="CustomSourceNameInput"
value={customSourceNameValue}
onChange={handleNameChange}
/>
</EuiFormRow>
<EuiSpacer />
<EuiFormRow>
<EuiButton
color="primary"
fill
type="submit"
isLoading={buttonLoading}
data-test-subj="CreateCustomButton"
>
{serviceType === 'custom' ? (
CONFIG_CUSTOM_BUTTON
) : (
<FormattedMessage
id="xpack.enterpriseSearch.workplaceSearch.contentSource.configCustom.createNamedSourceButtonLabel"
defaultMessage="Create {name} source"
values={{
name,
}}
/>
)}
</EuiButton>
</EuiFormRow>
</EuiForm>
</form>
</>
);
};

View file

@ -272,62 +272,6 @@ export const SAVE_CUSTOM_BODY1 = i18n.translate(
}
);
export const SAVE_CUSTOM_BODY2 = i18n.translate(
'xpack.enterpriseSearch.workplaceSearch.contentSource.saveCustom.body2',
{
defaultMessage: 'Be sure to copy your Source Identifier below.',
}
);
export const SAVE_CUSTOM_RETURN_BUTTON = i18n.translate(
'xpack.enterpriseSearch.workplaceSearch.contentSource.saveCustom.return.button',
{
defaultMessage: 'Return to Sources',
}
);
export const SAVE_CUSTOM_VISUAL_WALKTHROUGH_TITLE = i18n.translate(
'xpack.enterpriseSearch.workplaceSearch.contentSource.saveCustom.visualWalkthrough.title',
{
defaultMessage: 'Visual Walkthrough',
}
);
export const SAVE_CUSTOM_VISUAL_WALKTHROUGH_LINK = i18n.translate(
'xpack.enterpriseSearch.workplaceSearch.contentSource.saveCustom.visualWalkthrough.link',
{
defaultMessage: 'Check out the documentation',
}
);
export const SAVE_CUSTOM_STYLING_RESULTS_TITLE = i18n.translate(
'xpack.enterpriseSearch.workplaceSearch.contentSource.saveCustom.stylingResults.title',
{
defaultMessage: 'Styling Results',
}
);
export const SAVE_CUSTOM_STYLING_RESULTS_LINK = i18n.translate(
'xpack.enterpriseSearch.workplaceSearch.contentSource.saveCustom.stylingResults.link',
{
defaultMessage: 'Display Settings',
}
);
export const SAVE_CUSTOM_DOC_PERMISSIONS_TITLE = i18n.translate(
'xpack.enterpriseSearch.workplaceSearch.contentSource.saveCustom.docPermissions.title',
{
defaultMessage: 'Set document-level permissions',
}
);
export const SAVE_CUSTOM_DOC_PERMISSIONS_LINK = i18n.translate(
'xpack.enterpriseSearch.workplaceSearch.contentSource.saveCustom.docPermissions.link',
{
defaultMessage: 'Document-level permissions',
}
);
export const INCLUDED_FEATURES_TITLE = i18n.translate(
'xpack.enterpriseSearch.workplaceSearch.contentSource.includedFeaturesTitle',
{

View file

@ -1,55 +0,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 { setMockValues } from '../../../../../__mocks__/kea_logic';
import React from 'react';
import { shallow } from 'enzyme';
import { EuiPanel, EuiTitle } from '@elastic/eui';
import { EuiLinkTo } from '../../../../../shared/react_router_helpers';
import { LicenseBadge } from '../../../../components/shared/license_badge';
import { staticCustomSourceData } from '../../source_data';
import { SaveCustom } from './save_custom';
describe('SaveCustom', () => {
const mockValues = {
newCustomSource: {
id: 'id',
accessToken: 'token',
name: 'name',
},
sourceData: staticCustomSourceData,
isOrganization: true,
hasPlatinumLicense: true,
};
beforeEach(() => {
setMockValues(mockValues);
});
it('renders', () => {
const wrapper = shallow(<SaveCustom />);
expect(wrapper.find(EuiPanel)).toHaveLength(1);
expect(wrapper.find(EuiTitle)).toHaveLength(4);
expect(wrapper.find(EuiLinkTo)).toHaveLength(1);
expect(wrapper.find(LicenseBadge)).toHaveLength(0);
});
it('renders platinum license badge if license is not present', () => {
setMockValues({ ...mockValues, hasPlatinumLicense: false });
const wrapper = shallow(<SaveCustom />);
expect(wrapper.find(LicenseBadge)).toHaveLength(1);
expect(wrapper.find(EuiTitle)).toHaveLength(4);
expect(wrapper.find(EuiLinkTo)).toHaveLength(1);
});
});

View file

@ -1,236 +0,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 React from 'react';
import { useValues } from 'kea';
import {
EuiFlexGroup,
EuiFlexItem,
EuiHorizontalRule,
EuiIcon,
EuiSpacer,
EuiText,
EuiTextAlign,
EuiTitle,
EuiLink,
EuiPanel,
EuiCode,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n-react';
import { docLinks } from '../../../../../shared/doc_links';
import { LicensingLogic } from '../../../../../shared/licensing';
import { EuiLinkTo } from '../../../../../shared/react_router_helpers';
import { AppLogic } from '../../../../app_logic';
import { LicenseBadge } from '../../../../components/shared/license_badge';
import {
SOURCES_PATH,
SOURCE_DISPLAY_SETTINGS_PATH,
getContentSourcePath,
getSourcesPath,
} from '../../../../routes';
import { LEARN_CUSTOM_FEATURES_BUTTON } from '../../constants';
import { SourceIdentifier } from '../source_identifier';
import { AddCustomSourceLogic } from './add_custom_source_logic';
import { AddSourceHeader } from './add_source_header';
import {
SAVE_CUSTOM_BODY1,
SAVE_CUSTOM_BODY2,
SAVE_CUSTOM_RETURN_BUTTON,
SAVE_CUSTOM_VISUAL_WALKTHROUGH_TITLE,
SAVE_CUSTOM_VISUAL_WALKTHROUGH_LINK,
SAVE_CUSTOM_STYLING_RESULTS_TITLE,
SAVE_CUSTOM_STYLING_RESULTS_LINK,
SAVE_CUSTOM_DOC_PERMISSIONS_TITLE,
SAVE_CUSTOM_DOC_PERMISSIONS_LINK,
} from './constants';
export const SaveCustom: React.FC = () => {
const { newCustomSource, sourceData } = useValues(AddCustomSourceLogic);
const { isOrganization } = useValues(AppLogic);
const { hasPlatinumLicense } = useValues(LicensingLogic);
const {
serviceType,
configuration: { githubRepository, documentationUrl },
name,
categories = [],
} = sourceData;
return (
<>
<AddSourceHeader name={name} serviceType={serviceType} categories={categories} />
<EuiSpacer />
<EuiFlexGroup direction="row">
<EuiFlexItem grow={2}>
<EuiPanel paddingSize="l" hasShadow={false} color="subdued">
<EuiFlexGroup direction="column" alignItems="center" responsive={false}>
<EuiFlexItem>
<EuiIcon type="checkInCircleFilled" color="#42CC89" size="xxl" />
</EuiFlexItem>
<EuiFlexItem>
<EuiTitle size="l">
<EuiTextAlign textAlign="center">
<h1>
{i18n.translate(
'xpack.enterpriseSearch.workplaceSearch.contentSource.saveCustom.heading',
{
defaultMessage: '{name} Created',
values: { name: newCustomSource.name },
}
)}
</h1>
</EuiTextAlign>
</EuiTitle>
<EuiText grow={false}>
<EuiTextAlign textAlign="center">
{SAVE_CUSTOM_BODY1}
<EuiSpacer size="s" />
{serviceType !== 'custom' && githubRepository && (
<>
<FormattedMessage
id="xpack.enterpriseSearch.workplaceSearch.contentSource.saveCustom.repositoryInstructions"
defaultMessage="First you'll need to clone and deploy this repository"
/>
<br />
<EuiCode>
<EuiLinkTo to={`https://github.com/${githubRepository}`}>
{githubRepository}
</EuiLinkTo>
</EuiCode>
<EuiSpacer size="s" />
</>
)}
{SAVE_CUSTOM_BODY2}
<br />
<EuiLinkTo to={getSourcesPath(SOURCES_PATH, isOrganization)}>
{SAVE_CUSTOM_RETURN_BUTTON}
</EuiLinkTo>
</EuiTextAlign>
</EuiText>
</EuiFlexItem>
</EuiFlexGroup>
<EuiHorizontalRule />
<EuiSpacer size="s" />
<SourceIdentifier id={newCustomSource.id} />
</EuiPanel>
</EuiFlexItem>
<EuiFlexItem grow={1}>
<EuiFlexGroup justifyContent="flexStart" alignItems="flexStart" responsive={false}>
<EuiFlexItem grow={false}>
<EuiSpacer size="s" />
<div>
<EuiTitle size="xs">
<h4>{SAVE_CUSTOM_VISUAL_WALKTHROUGH_TITLE}</h4>
</EuiTitle>
<EuiSpacer size="xs" />
<EuiText color="subdued" size="s">
<p>
{serviceType === 'custom' ? (
<FormattedMessage
id="xpack.enterpriseSearch.workplaceSearch.contentSource.saveCustom.documentation.text"
defaultMessage="{link} to learn more about Custom API Sources."
values={{
link: (
<EuiLink target="_blank" href={documentationUrl}>
{SAVE_CUSTOM_VISUAL_WALKTHROUGH_LINK}
</EuiLink>
),
}}
/>
) : (
<FormattedMessage
id="xpack.enterpriseSearch.workplaceSearch.contentSource.saveCustom.namedSourceDocumentation.text"
defaultMessage="{link} to learn more about deploying a {name} source."
values={{
link: (
<EuiLink target="_blank" href={documentationUrl}>
{SAVE_CUSTOM_VISUAL_WALKTHROUGH_LINK}
</EuiLink>
),
name,
}}
/>
)}
</p>
</EuiText>
</div>
<EuiSpacer />
<div>
<EuiTitle size="xs">
<h4>{SAVE_CUSTOM_STYLING_RESULTS_TITLE}</h4>
</EuiTitle>
<EuiSpacer size="xs" />
<EuiText color="subdued" size="s">
<p>
<FormattedMessage
id="xpack.enterpriseSearch.workplaceSearch.contentSource.saveCustom.displaySettings.text"
defaultMessage="Use {link} to customize how your documents will appear within your search results. Workplace Search will use fields in alphabetical order by default."
values={{
link: (
<EuiLinkTo
to={getContentSourcePath(
SOURCE_DISPLAY_SETTINGS_PATH,
newCustomSource.id,
isOrganization
)}
>
{SAVE_CUSTOM_STYLING_RESULTS_LINK}
</EuiLinkTo>
),
}}
/>
</p>
</EuiText>
</div>
<EuiSpacer />
<div>
<EuiSpacer size="s" />
{!hasPlatinumLicense && <LicenseBadge />}
<EuiSpacer size="s" />
<EuiTitle size="xs">
<h4>{SAVE_CUSTOM_DOC_PERMISSIONS_TITLE}</h4>
</EuiTitle>
<EuiSpacer size="xs" />
<EuiText color="subdued" size="s">
<p>
<FormattedMessage
id="xpack.enterpriseSearch.workplaceSearch.contentSource.saveCustom.permissions.text"
defaultMessage="{link} manage content access on individual or group attributes. Allow or deny access to specific documents."
values={{
link: (
<EuiLink
target="_blank"
href={docLinks.workplaceSearchCustomSourcePermissions}
>
{SAVE_CUSTOM_DOC_PERMISSIONS_LINK}
</EuiLink>
),
}}
/>
</p>
</EuiText>
<EuiSpacer size="xs" />
{!hasPlatinumLicense && (
<EuiText size="s">
<EuiLink target="_blank" href={docLinks.licenseManagement}>
{LEARN_CUSTOM_FEATURES_BUTTON}
</EuiLink>
</EuiText>
)}
</div>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
</EuiFlexGroup>
</>
);
};

View file

@ -14,14 +14,11 @@ import {
EuiCopy,
EuiButtonIcon,
EuiFieldText,
EuiSpacer,
} from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n-react';
import { EuiLinkTo } from '../../../../shared/react_router_helpers';
import { i18n } from '@kbn/i18n';
import { API_KEY_LABEL, COPY_TOOLTIP, COPIED_TOOLTIP } from '../../../constants';
import { API_KEYS_PATH } from '../../../routes';
import { COPY_TOOLTIP, COPIED_TOOLTIP } from '../../../constants';
import { ID_LABEL } from '../constants';
@ -50,24 +47,17 @@ export const SourceIdentifier: React.FC<Props> = ({ id }) => (
</EuiCopy>
</EuiFlexItem>
<EuiFlexItem>
<EuiFieldText value={id} readOnly />
<EuiFieldText
value={id}
readOnly
aria-label={i18n.translate(
'xpack.enterpriseSearch.workplaceSearch.sourceIdentifier.sourceIdentifierFieldLabel',
{
defaultMessage: 'Source Identifier',
}
)}
/>
</EuiFlexItem>
</EuiFlexGroup>
<EuiSpacer size="s" />
<EuiText size="s">
<p>
<FormattedMessage
id="xpack.enterpriseSearch.workplaceSearch.sources.identifier.helpText"
defaultMessage="Use the Source Identifier with an {apiKeyLink} to sync documents for this custom source."
values={{
apiKeyLink: (
<EuiLinkTo target="_blank" to={API_KEYS_PATH}>
{API_KEY_LABEL}
</EuiLinkTo>
),
}}
/>
</p>
</EuiText>
</>
);

View file

@ -560,14 +560,7 @@ export const staticSourceData: SourceDataItem[] = [
isPublicKey: false,
hasOauthRedirect: false,
needsBaseUrl: false,
// helpText: i18n.translate( // TODO updatae this
// 'xpack.enterpriseSearch.workplaceSearch.sources.helpText.sharepointServer',
// {
// defaultMessage:
// "Here is some help text. It should probably give the user a heads up that they're going to have to deploy some code.",
// }
// ),
documentationUrl: docLinks.workplaceSearchCustomSources, // TODO update this
documentationUrl: docLinks.workplaceSearchSharePointServer,
applicationPortalUrl: '',
githubRepository: 'elastic/enterprise-search-sharepoint-server-connector',
},
@ -638,10 +631,6 @@ export const staticCustomSourceData: SourceDataItem = {
isPublicKey: false,
hasOauthRedirect: false,
needsBaseUrl: false,
helpText: i18n.translate('xpack.enterpriseSearch.workplaceSearch.sources.helpText.custom', {
defaultMessage:
'To create a Custom API Source, provide a human-readable and descriptive name. The name will appear as-is in the various search experiences and management interfaces.',
}),
documentationUrl: docLinks.workplaceSearchCustomSources,
applicationPortalUrl: '',
},

View file

@ -9395,15 +9395,7 @@
"xpack.enterpriseSearch.workplaceSearch.contentSource.saveConfig.oauthStep1": "Créer une application OAuth dans le compte {sourceName} de votre organisation",
"xpack.enterpriseSearch.workplaceSearch.contentSource.saveConfig.oauthStep2": "Fournir les informations de configuration appropriées",
"xpack.enterpriseSearch.workplaceSearch.contentSource.saveCustom.body1": "Vos points de terminaison sont prêts à accepter les requêtes.",
"xpack.enterpriseSearch.workplaceSearch.contentSource.saveCustom.body2": "Veillez à copier vos clés d'API ci-dessous.",
"xpack.enterpriseSearch.workplaceSearch.contentSource.saveCustom.displaySettings.text": "Utilisez {link} pour personnaliser le mode d'affichage de vos documents dans vos résultats de recherche. Par défaut, Workplace Search utilisera les champs par ordre alphabétique.",
"xpack.enterpriseSearch.workplaceSearch.contentSource.saveCustom.docPermissions.title": "Définir les autorisations de niveau document",
"xpack.enterpriseSearch.workplaceSearch.contentSource.saveCustom.documentation.text": "{link} pour en savoir plus sur les sources d'API personnalisées.",
"xpack.enterpriseSearch.workplaceSearch.contentSource.saveCustom.heading": "{name} créé",
"xpack.enterpriseSearch.workplaceSearch.contentSource.saveCustom.permissions.text": "{link} gèrent le contenu de l'accès au contenu selon les attributs individuels ou de groupe. Autorisez ou refusez l'accès à des documents spécifiques.",
"xpack.enterpriseSearch.workplaceSearch.contentSource.saveCustom.return.button": "Retour aux sources",
"xpack.enterpriseSearch.workplaceSearch.contentSource.saveCustom.stylingResults.title": "Résultats de styles",
"xpack.enterpriseSearch.workplaceSearch.contentSource.saveCustom.visualWalkthrough.title": "Présentation visuelle",
"xpack.enterpriseSearch.workplaceSearch.contentSource.schema.addField.button": "Ajouter un champ",
"xpack.enterpriseSearch.workplaceSearch.contentSource.schema.empty.description": "Un schéma est créé à votre place une fois que vous avez indexé quelques documents. Cliquez ci-dessous pour créer des champs de schéma à l'avance.",
"xpack.enterpriseSearch.workplaceSearch.contentSource.schema.empty.title": "La source de contenu ne possède pas de schéma",

View file

@ -11076,18 +11076,7 @@
"xpack.enterpriseSearch.workplaceSearch.contentSource.saveConfig.oauthStep1": "組織の{sourceName}アカウントでOAuthアプリを作成する",
"xpack.enterpriseSearch.workplaceSearch.contentSource.saveConfig.oauthStep2": "適切な構成情報を入力する",
"xpack.enterpriseSearch.workplaceSearch.contentSource.saveCustom.body1": "エンドポイントは要求を承認できます。",
"xpack.enterpriseSearch.workplaceSearch.contentSource.saveCustom.body2": "必ず以下のソースIDをコピーしてください。",
"xpack.enterpriseSearch.workplaceSearch.contentSource.saveCustom.displaySettings.text": "{link}を使用して、検索結果内でドキュメントが表示される方法をカスタマイズします。デフォルトでは、Workplace Searchは英字順でフィールドを使用します。",
"xpack.enterpriseSearch.workplaceSearch.contentSource.saveCustom.docPermissions.link": "ドキュメントレベルのアクセス権",
"xpack.enterpriseSearch.workplaceSearch.contentSource.saveCustom.docPermissions.title": "ドキュメントレベルのアクセス権を設定",
"xpack.enterpriseSearch.workplaceSearch.contentSource.saveCustom.documentation.text": "カスタムAPIソースの詳細については、{link}。",
"xpack.enterpriseSearch.workplaceSearch.contentSource.saveCustom.heading": "{name}が作成されました",
"xpack.enterpriseSearch.workplaceSearch.contentSource.saveCustom.permissions.text": "{link}は個別またはグループの属性でコンテンツアクセスコンテンツを管理します。特定のドキュメントへのアクセスを許可または拒否。",
"xpack.enterpriseSearch.workplaceSearch.contentSource.saveCustom.return.button": "ソースに戻る",
"xpack.enterpriseSearch.workplaceSearch.contentSource.saveCustom.stylingResults.link": "表示設定",
"xpack.enterpriseSearch.workplaceSearch.contentSource.saveCustom.stylingResults.title": "スタイルの結果",
"xpack.enterpriseSearch.workplaceSearch.contentSource.saveCustom.visualWalkthrough.link": "ドキュメントを確認",
"xpack.enterpriseSearch.workplaceSearch.contentSource.saveCustom.visualWalkthrough.title": "表示の確認",
"xpack.enterpriseSearch.workplaceSearch.contentSource.schema.addField.button": "フィールドの追加",
"xpack.enterpriseSearch.workplaceSearch.contentSource.schema.empty.description": "一部のドキュメントにインデックスを作成すると、スキーマが作成されます。あらかじめスキーマフィールドを作成するには、以下をクリックします。",
"xpack.enterpriseSearch.workplaceSearch.contentSource.schema.empty.title": "コンテンツソースにはスキーマがありません",
@ -11443,7 +11432,6 @@
"xpack.enterpriseSearch.workplaceSearch.sources.groupAccess.title": "グループアクセス",
"xpack.enterpriseSearch.workplaceSearch.sources.helpText.custom": "カスタムAPIソースを作成するには、人間が読み取れるわかりやすい名前を入力します。この名前はさまざまな検索エクスペリエンスと管理インターフェースでそのまま表示されます。",
"xpack.enterpriseSearch.workplaceSearch.sources.id.label": "ソース識別子",
"xpack.enterpriseSearch.workplaceSearch.sources.identifier.helpText": "ソースIDと{apiKeyLink}を使用して、このカスタムソースのドキュメントを同期します。",
"xpack.enterpriseSearch.workplaceSearch.sources.incrementalSyncDescription": "前回の同期ジョブ以降に発生したドキュメント/更新を取得します",
"xpack.enterpriseSearch.workplaceSearch.sources.incrementalSyncLabel": "差分同期",
"xpack.enterpriseSearch.workplaceSearch.sources.items.header": "アイテム",

View file

@ -11097,18 +11097,7 @@
"xpack.enterpriseSearch.workplaceSearch.contentSource.saveConfig.oauthStep1": "在组织的 {sourceName} 帐户中创建 OAuth 应用",
"xpack.enterpriseSearch.workplaceSearch.contentSource.saveConfig.oauthStep2": "提供适当的配置信息",
"xpack.enterpriseSearch.workplaceSearch.contentSource.saveCustom.body1": "您的终端已准备好接受请求。",
"xpack.enterpriseSearch.workplaceSearch.contentSource.saveCustom.body2": "确保复制下面的源标识符。",
"xpack.enterpriseSearch.workplaceSearch.contentSource.saveCustom.displaySettings.text": "请使用 {link} 定制您的文档在搜索结果内显示的方式。Workplace Search 默认按字母顺序使用字段。",
"xpack.enterpriseSearch.workplaceSearch.contentSource.saveCustom.docPermissions.link": "文档级权限",
"xpack.enterpriseSearch.workplaceSearch.contentSource.saveCustom.docPermissions.title": "设置文档级权限",
"xpack.enterpriseSearch.workplaceSearch.contentSource.saveCustom.documentation.text": "{link}以详细了解定制 API 源。",
"xpack.enterpriseSearch.workplaceSearch.contentSource.saveCustom.heading": "{name} 已创建",
"xpack.enterpriseSearch.workplaceSearch.contentSource.saveCustom.permissions.text": "{link} 管理有关单个属性或组属性的内容访问内容。允许或拒绝对特定文档的访问。",
"xpack.enterpriseSearch.workplaceSearch.contentSource.saveCustom.return.button": "返回到源",
"xpack.enterpriseSearch.workplaceSearch.contentSource.saveCustom.stylingResults.link": "显示设置",
"xpack.enterpriseSearch.workplaceSearch.contentSource.saveCustom.stylingResults.title": "正在为结果应用样式",
"xpack.enterpriseSearch.workplaceSearch.contentSource.saveCustom.visualWalkthrough.link": "查阅文档",
"xpack.enterpriseSearch.workplaceSearch.contentSource.saveCustom.visualWalkthrough.title": "直观的演练",
"xpack.enterpriseSearch.workplaceSearch.contentSource.schema.addField.button": "添加字段",
"xpack.enterpriseSearch.workplaceSearch.contentSource.schema.empty.description": "您索引一些文档后,系统便会为您创建架构。单击下面,以提前创建架构字段。",
"xpack.enterpriseSearch.workplaceSearch.contentSource.schema.empty.title": "内容源没有架构",
@ -11464,7 +11453,6 @@
"xpack.enterpriseSearch.workplaceSearch.sources.groupAccess.title": "组访问权限",
"xpack.enterpriseSearch.workplaceSearch.sources.helpText.custom": "要创建定制 API 源,请提供可人工读取的描述性名称。名称在各种搜索体验和管理界面中都原样显示。",
"xpack.enterpriseSearch.workplaceSearch.sources.id.label": "源标识符",
"xpack.enterpriseSearch.workplaceSearch.sources.identifier.helpText": "将源标识符用于 {apiKeyLink},以同步此定制源的文档。",
"xpack.enterpriseSearch.workplaceSearch.sources.incrementalSyncDescription": "检索自上次同步作业以来的文档/更新",
"xpack.enterpriseSearch.workplaceSearch.sources.incrementalSyncLabel": "增量同步",
"xpack.enterpriseSearch.workplaceSearch.sources.items.header": "项",