diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_settings.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_settings.test.tsx index 883c8631365ebe..83cf21ce862331 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_settings.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_settings.test.tsx @@ -105,84 +105,6 @@ describe('SourceSettings', () => { ); }); - it('handles disabling synchronization', () => { - const wrapper = shallow(); - - const synchronizeSwitch = wrapper.find('[data-test-subj="SynchronizeToggle"]').first(); - const event = { target: { checked: false } }; - synchronizeSwitch.prop('onChange')?.(event as any); - - wrapper.find('[data-test-subj="SaveSyncControlsButton"]').simulate('click'); - - expect(updateContentSource).toHaveBeenCalledWith(fullContentSources[0].id, { - indexing: { - enabled: false, - features: { - content_extraction: { enabled: true }, - thumbnails: { enabled: true }, - }, - }, - }); - }); - - it('handles disabling thumbnails', () => { - const wrapper = shallow(); - - const thumbnailsSwitch = wrapper.find('[data-test-subj="ThumbnailsToggle"]').first(); - const event = { target: { checked: false } }; - thumbnailsSwitch.prop('onChange')?.(event as any); - - wrapper.find('[data-test-subj="SaveSyncControlsButton"]').simulate('click'); - - expect(updateContentSource).toHaveBeenCalledWith(fullContentSources[0].id, { - indexing: { - enabled: true, - features: { - content_extraction: { enabled: true }, - thumbnails: { enabled: false }, - }, - }, - }); - }); - - it('handles disabling content extraction', () => { - const wrapper = shallow(); - - const contentExtractionSwitch = wrapper - .find('[data-test-subj="ContentExtractionToggle"]') - .first(); - const event = { target: { checked: false } }; - contentExtractionSwitch.prop('onChange')?.(event as any); - - wrapper.find('[data-test-subj="SaveSyncControlsButton"]').simulate('click'); - - expect(updateContentSource).toHaveBeenCalledWith(fullContentSources[0].id, { - indexing: { - enabled: true, - features: { - content_extraction: { enabled: false }, - thumbnails: { enabled: true }, - }, - }, - }); - }); - - it('disables the thumbnails switch when globally disabled', () => { - setMockValues({ - ...mockValues, - contentSource: { - ...fullContentSources[0], - areThumbnailsConfigEnabled: false, - }, - }); - - const wrapper = shallow(); - - const synchronizeSwitch = wrapper.find('[data-test-subj="ThumbnailsToggle"]'); - - expect(synchronizeSwitch.prop('disabled')).toEqual(true); - }); - describe('DownloadDiagnosticsButton', () => { it('renders for org with correct href', () => { const wrapper = shallow(); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_settings.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_settings.tsx index 36a6374dddcd63..f2d74f00fadf4a 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_settings.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_settings.tsx @@ -17,8 +17,6 @@ import { EuiFlexGroup, EuiFlexItem, EuiFormRow, - EuiSpacer, - EuiSwitch, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; @@ -50,12 +48,6 @@ import { SYNC_DIAGNOSTICS_TITLE, SYNC_DIAGNOSTICS_DESCRIPTION, SYNC_DIAGNOSTICS_BUTTON, - SYNC_MANAGEMENT_TITLE, - SYNC_MANAGEMENT_DESCRIPTION, - SYNC_MANAGEMENT_SYNCHRONIZE_LABEL, - SYNC_MANAGEMENT_THUMBNAILS_LABEL, - SYNC_MANAGEMENT_THUMBNAILS_GLOBAL_CONFIG_LABEL, - SYNC_MANAGEMENT_CONTENT_EXTRACTION_LABEL, } from '../constants'; import { staticSourceData } from '../source_data'; import { SourceLogic } from '../source_logic'; @@ -70,22 +62,7 @@ export const SourceSettings: React.FC = () => { const { getSourceConfigData } = useActions(AddSourceLogic); const { - contentSource: { - name, - id, - serviceType, - custom: isCustom, - isIndexedSource, - areThumbnailsConfigEnabled, - isOauth1, - indexing: { - enabled, - features: { - contentExtraction: { enabled: contentExtractionEnabled }, - thumbnails: { enabled: thumbnailsEnabled }, - }, - }, - }, + contentSource: { name, id, serviceType, isOauth1 }, buttonLoading, } = useValues(SourceLogic); @@ -109,11 +86,6 @@ export const SourceSettings: React.FC = () => { const hideConfirm = () => setModalVisibility(false); const showConfig = isOrganization && !isEmpty(configuredFields); - const showSyncControls = isOrganization && isIndexedSource && !isCustom; - - const [synchronizeChecked, setSynchronize] = useState(enabled); - const [thumbnailsChecked, setThumbnails] = useState(thumbnailsEnabled); - const [contentExtractionChecked, setContentExtraction] = useState(contentExtractionEnabled); const { clientId, clientSecret, publicKey, consumerKey, baseUrl } = configuredFields || {}; @@ -130,18 +102,6 @@ export const SourceSettings: React.FC = () => { updateContentSource(id, { name: inputValue }); }; - const submitSyncControls = () => { - updateContentSource(id, { - indexing: { - enabled: synchronizeChecked, - features: { - content_extraction: { enabled: contentExtractionChecked }, - thumbnails: { enabled: thumbnailsChecked }, - }, - }, - }); - }; - const handleSourceRemoval = () => { /** * The modal was just hanging while the UI waited for the server to respond. @@ -221,58 +181,6 @@ export const SourceSettings: React.FC = () => { )} - {showSyncControls && ( - - - - setSynchronize(e.target.checked)} - label={SYNC_MANAGEMENT_SYNCHRONIZE_LABEL} - data-test-subj="SynchronizeToggle" - /> - - - - - - setThumbnails(e.target.checked)} - label={ - areThumbnailsConfigEnabled - ? SYNC_MANAGEMENT_THUMBNAILS_LABEL - : SYNC_MANAGEMENT_THUMBNAILS_GLOBAL_CONFIG_LABEL - } - disabled={!areThumbnailsConfigEnabled} - data-test-subj="ThumbnailsToggle" - /> - - - - - setContentExtraction(e.target.checked)} - label={SYNC_MANAGEMENT_CONTENT_EXTRACTION_LABEL} - data-test-subj="ContentExtractionToggle" - /> - - - - - - - {SAVE_CHANGES_BUTTON} - - - - - )} { - options.push({ - label: DAYS_OF_WEEK_LABELS[day.toUpperCase() as keyof typeof DAYS_OF_WEEK_LABELS], - value: day, - }); - return options; -}, [] as Array>); +const dayPickerOptions = DAYS_OF_WEEK_VALUES.map((day) => ({ + label: DAYS_OF_WEEK_LABELS[day.toUpperCase() as keyof typeof DAYS_OF_WEEK_LABELS], + value: day, +})); export const BlockedWindowItem: React.FC = ({ blockedWindow }) => { const handleSyncTypeChange = () => '#TODO'; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/synchronization/blocked_window_tab.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/synchronization/blocked_window_tab.test.tsx index 0d5183b5e95e13..7cada1d39fb6e8 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/synchronization/blocked_window_tab.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/synchronization/blocked_window_tab.test.tsx @@ -7,6 +7,7 @@ import '../../../../../__mocks__/shallow_useeffect.mock'; import { setMockActions, setMockValues } from '../../../../../__mocks__/kea_logic'; +import { fullContentSources } from '../../../../__mocks__/content_sources.mock'; import { blockedWindow } from './__mocks__/syncronization.mock'; import React from 'react'; @@ -25,6 +26,7 @@ describe('BlockedWindows', () => { }; const mockValues = { blockedWindows: [blockedWindow], + contentSource: fullContentSources[0], }; beforeEach(() => { diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/synchronization/blocked_window_tab.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/synchronization/blocked_window_tab.tsx index 474bf4cab2a8e3..f0227f76d4aa59 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/synchronization/blocked_window_tab.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/synchronization/blocked_window_tab.tsx @@ -13,13 +13,15 @@ import { EuiButton, EuiEmptyPrompt, EuiSpacer } from '@elastic/eui'; import { ADD_LABEL } from '../../../../constants'; import { BLOCKED_EMPTY_STATE_TITLE, BLOCKED_EMPTY_STATE_DESCRIPTION } from '../../constants'; +import { SourceLogic } from '../../source_logic'; import { BlockedWindowItem } from './blocked_window_item'; import { SynchronizationLogic } from './synchronization_logic'; export const BlockedWindows: React.FC = () => { - const { blockedWindows } = useValues(SynchronizationLogic); - const { addBlockedWindow } = useActions(SynchronizationLogic); + const { contentSource } = useValues(SourceLogic); + const { blockedWindows } = useValues(SynchronizationLogic({ contentSource })); + const { addBlockedWindow } = useActions(SynchronizationLogic({ contentSource })); const hasBlockedWindows = blockedWindows.length > 0; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/synchronization/frequency.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/synchronization/frequency.test.tsx index 08de4b41758a2c..283c9a9cebbbd6 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/synchronization/frequency.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/synchronization/frequency.test.tsx @@ -7,6 +7,7 @@ import '../../../../../__mocks__/shallow_useeffect.mock'; import { setMockActions, setMockValues } from '../../../../../__mocks__/kea_logic'; +import { fullContentSources } from '../../../../__mocks__/content_sources.mock'; import React from 'react'; @@ -23,7 +24,9 @@ describe('Frequency', () => { const mockActions = { handleSelectedTabChanged, }; - const mockValues = {}; + const mockValues = { + contentSource: fullContentSources[0], + }; beforeEach(() => { setMockActions(mockActions); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/synchronization/frequency.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/synchronization/frequency.tsx index fb19c84ecfdd12..3ca34f49604743 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/synchronization/frequency.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/synchronization/frequency.tsx @@ -7,7 +7,7 @@ import React from 'react'; -import { useActions } from 'kea'; +import { useActions, useValues } from 'kea'; import { EuiButton, @@ -31,6 +31,7 @@ import { DIFFERENT_SYNC_TYPES_LINK_LABEL, SYNC_BEST_PRACTICES_LINK_LABEL, } from '../../constants'; +import { SourceLogic } from '../../source_logic'; import { SourceLayout } from '../source_layout'; import { BlockedWindows } from './blocked_window_tab'; @@ -42,7 +43,8 @@ interface FrequencyProps { } export const Frequency: React.FC = ({ tabId }) => { - const { handleSelectedTabChanged } = useActions(SynchronizationLogic); + const { contentSource } = useValues(SourceLogic); + const { handleSelectedTabChanged } = useActions(SynchronizationLogic({ contentSource })); const tabs = [ { diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/synchronization/frequency_item.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/synchronization/frequency_item.test.tsx index fb346ad96117ec..ce295b467a09df 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/synchronization/frequency_item.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/synchronization/frequency_item.test.tsx @@ -8,21 +8,24 @@ import React from 'react'; import { shallow } from 'enzyme'; +import moment from 'moment'; import { EuiFieldNumber, EuiSuperSelect } from '@elastic/eui'; import { FrequencyItem } from './frequency_item'; describe('FrequencyItem', () => { + const estimate = { + duration: 'PT3D', + nextStart: '2021-09-27T21:39:24+00:00', + lastRun: '2021-09-25T21:39:24+00:00', + }; + const props = { label: 'Item', description: 'My item', duration: 'PT2D', - estimate: { - duration: 'PT3D', - nextStart: '2021-09-27T21:39:24+00:00', - lastRun: '2021-09-25T21:39:24+00:00', - }, + estimate, }; it('renders', () => { @@ -60,5 +63,25 @@ describe('FrequencyItem', () => { expect(wrapper.find(EuiFieldNumber).prop('value')).toEqual(1); expect(wrapper.find(EuiSuperSelect).prop('valueOfSelected')).toEqual('minutes'); }); + + it('handles "nextStart" that is in past', () => { + const wrapper = shallow(); + + expect( + (wrapper.find('[data-test-subj="nextStartSummary"]').prop('values') as any)!.nextStartTime + ).toEqual('as soon as the currently running job finishes'); + }); + + it('handles "nextStart" that is in future', () => { + const estimateWithPastNextStart = { + ...estimate, + nextStart: moment().add(2, 'days').format(), + }; + const wrapper = shallow(); + + expect( + (wrapper.find('[data-test-subj="nextStartSummary"]').prop('values') as any)!.nextStartTime + ).toEqual('in 2 days'); + }); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/synchronization/frequency_item.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/synchronization/frequency_item.tsx index 4e9eec28dc1ebb..38f85ff2accafc 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/synchronization/frequency_item.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/synchronization/frequency_item.tsx @@ -27,6 +27,8 @@ import { } from '../../../../../shared/constants'; import { SyncEstimate } from '../../../../types'; +import { NEXT_SYNC_RUNNING_MESSAGE } from '../../constants'; + interface Props { label: string; description: string; @@ -53,6 +55,8 @@ export const FrequencyItem: React.FC = ({ label, description, duration, e const [interval, unit] = formatDuration(duration); const { lastRun, nextStart, duration: durationEstimate } = estimate; const estimateDisplay = durationEstimate && moment.duration(durationEstimate).humanize(); + const nextStartIsPast = moment().isAfter(nextStart); + const nextStartTime = nextStartIsPast ? NEXT_SYNC_RUNNING_MESSAGE : moment(nextStart).fromNow(); const onChange = () => '#TODO'; @@ -86,6 +90,7 @@ export const FrequencyItem: React.FC = ({ label, description, duration, e const nextStartSummary = ( = ({ label, description, duration, e /> ), - nextStartTime: moment(nextStart).fromNow(), + nextStartTime, }} /> ); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/synchronization/objects_and_assets.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/synchronization/objects_and_assets.test.tsx new file mode 100644 index 00000000000000..42a08084db4184 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/synchronization/objects_and_assets.test.tsx @@ -0,0 +1,86 @@ +/* + * 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 '../../../../../__mocks__/shallow_useeffect.mock'; +import { setMockActions, setMockValues } from '../../../../../__mocks__/kea_logic'; +import { fullContentSources } from '../../../../__mocks__/content_sources.mock'; +import { blockedWindow } from './__mocks__/syncronization.mock'; + +import React from 'react'; + +import { shallow } from 'enzyme'; + +import { EuiSwitch } from '@elastic/eui'; + +import { ObjectsAndAssets } from './objects_and_assets'; + +describe('ObjectsAndAssets', () => { + const setThumbnailsChecked = jest.fn(); + const setContentExtractionChecked = jest.fn(); + const updateSyncSettings = jest.fn(); + const resetSyncSettings = jest.fn(); + const contentSource = fullContentSources[0]; + + const mockActions = { + setThumbnailsChecked, + setContentExtractionChecked, + updateSyncSettings, + resetSyncSettings, + }; + const mockValues = { + dataLoading: false, + blockedWindows: [blockedWindow], + contentSource, + thumbnailsChecked: true, + contentExtractionChecked: true, + hasUnsavedObjectsAndAssetsChanges: false, + }; + + beforeEach(() => { + setMockActions(mockActions); + setMockValues(mockValues); + }); + + it('renders', () => { + const wrapper = shallow(); + + expect(wrapper.find(EuiSwitch)).toHaveLength(2); + }); + + it('handles thumbnails switch change', () => { + const wrapper = shallow(); + wrapper + .find('[data-test-subj="ThumbnailsToggle"]') + .simulate('change', { target: { checked: false } }); + + expect(setThumbnailsChecked).toHaveBeenCalledWith(false); + }); + + it('handles content extraction switch change', () => { + const wrapper = shallow(); + wrapper + .find('[data-test-subj="ContentExtractionToggle"]') + .simulate('change', { target: { checked: false } }); + + expect(setContentExtractionChecked).toHaveBeenCalledWith(false); + }); + + it('renders correct text when areThumbnailsConfigEnabled is false', () => { + setMockValues({ + ...mockValues, + contentSource: { + ...contentSource, + areThumbnailsConfigEnabled: false, + }, + }); + const wrapper = shallow(); + + expect(wrapper.find('[data-test-subj="ThumbnailsToggle"]').prop('label')).toEqual( + 'Sync thumbnails - disabled at global configuration level' + ); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/synchronization/objects_and_assets.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/synchronization/objects_and_assets.tsx index 4c2804459f1ba0..98abdb8bf67ea0 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/synchronization/objects_and_assets.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/synchronization/objects_and_assets.tsx @@ -7,33 +7,113 @@ import React from 'react'; -import { EuiHorizontalRule, EuiLink } from '@elastic/eui'; +import { useActions, useValues } from 'kea'; +import { + EuiButton, + EuiButtonEmpty, + EuiFlexGroup, + EuiFlexItem, + EuiHorizontalRule, + EuiLink, + EuiSpacer, + EuiSwitch, + EuiText, +} from '@elastic/eui'; + +import { SAVE_BUTTON_LABEL } from '../../../../../shared/constants'; +import { UnsavedChangesPrompt } from '../../../../../shared/unsaved_changes_prompt'; import { ViewContentHeader } from '../../../../components/shared/view_content_header'; -import { NAV } from '../../../../constants'; +import { NAV, RESET_BUTTON } from '../../../../constants'; import { OBJECTS_AND_ASSETS_DOCS_URL } from '../../../../routes'; import { + SYNC_MANAGEMENT_CONTENT_EXTRACTION_LABEL, + SYNC_MANAGEMENT_THUMBNAILS_LABEL, + SYNC_MANAGEMENT_THUMBNAILS_GLOBAL_CONFIG_LABEL, SOURCE_OBJECTS_AND_ASSETS_DESCRIPTION, SYNC_OBJECTS_TYPES_LINK_LABEL, + SOURCE_OBJECTS_AND_ASSETS_LABEL, + SYNC_UNSAVED_CHANGES_MESSAGE, } from '../../constants'; +import { SourceLogic } from '../../source_logic'; import { SourceLayout } from '../source_layout'; +import { SynchronizationLogic } from './synchronization_logic'; + export const ObjectsAndAssets: React.FC = () => { + const { contentSource, dataLoading } = useValues(SourceLogic); + const { thumbnailsChecked, contentExtractionChecked, hasUnsavedObjectsAndAssetsChanges } = + useValues(SynchronizationLogic({ contentSource })); + const { + setThumbnailsChecked, + setContentExtractionChecked, + updateSyncSettings, + resetSyncSettings, + } = useActions(SynchronizationLogic({ contentSource })); + + const { areThumbnailsConfigEnabled } = contentSource; + + const actions = ( + + + + {RESET_BUTTON} + + + + + {SAVE_BUTTON_LABEL} + + + + ); + return ( + {SYNC_OBJECTS_TYPES_LINK_LABEL} -
TODO
+ {SOURCE_OBJECTS_AND_ASSETS_LABEL} + + + + setThumbnailsChecked(e.target.checked)} + label={ + areThumbnailsConfigEnabled + ? SYNC_MANAGEMENT_THUMBNAILS_LABEL + : SYNC_MANAGEMENT_THUMBNAILS_GLOBAL_CONFIG_LABEL + } + disabled={!areThumbnailsConfigEnabled} + data-test-subj="ThumbnailsToggle" + /> + + + + + setContentExtractionChecked(e.target.checked)} + label={SYNC_MANAGEMENT_CONTENT_EXTRACTION_LABEL} + data-test-subj="ContentExtractionToggle" + /> + +
); }; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/synchronization/synchronization.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/synchronization/synchronization.test.tsx index 632af08611ca93..fb9cdc6916fa98 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/synchronization/synchronization.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/synchronization/synchronization.test.tsx @@ -5,7 +5,8 @@ * 2.0. */ -import { setMockValues } from '../../../../../__mocks__/kea_logic'; +import { setMockValues, setMockActions } from '../../../../../__mocks__/kea_logic'; +import { fullContentSources } from '../../../../__mocks__/content_sources.mock'; import React from 'react'; @@ -16,8 +17,15 @@ import { EuiLink, EuiCallOut, EuiSwitch } from '@elastic/eui'; import { Synchronization } from './synchronization'; describe('Synchronization', () => { + const updateSyncEnabled = jest.fn(); + const mockvalues = { contentSource: fullContentSources[0] }; + + beforeEach(() => { + setMockActions({ updateSyncEnabled }); + setMockValues(mockvalues); + }); + it('renders when config enabled', () => { - setMockValues({ contentSource: { isSyncConfigEnabled: true } }); const wrapper = shallow(); expect(wrapper.find(EuiLink)).toHaveLength(1); @@ -25,9 +33,16 @@ describe('Synchronization', () => { }); it('renders when config disabled', () => { - setMockValues({ contentSource: { isSyncConfigEnabled: false } }); + setMockValues({ contentSource: { isSyncConfigEnabled: false, indexing: { enabled: true } } }); const wrapper = shallow(); expect(wrapper.find(EuiCallOut)).toHaveLength(1); }); + + it('handles EuiSwitch change event', () => { + const wrapper = shallow(); + wrapper.find(EuiSwitch).simulate('change', { target: { checked: true } }); + + expect(updateSyncEnabled).toHaveBeenCalled(); + }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/synchronization/synchronization.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/synchronization/synchronization.tsx index 21daee8f26d401..21c44225615ea2 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/synchronization/synchronization.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/synchronization/synchronization.tsx @@ -7,7 +7,7 @@ import React from 'react'; -import { useValues } from 'kea'; +import { useActions, useValues } from 'kea'; import { EuiCallOut, EuiLink, EuiPanel, EuiSwitch, EuiSpacer, EuiText } from '@elastic/eui'; @@ -25,17 +25,23 @@ import { import { SourceLogic } from '../../source_logic'; import { SourceLayout } from '../source_layout'; +import { SynchronizationLogic } from './synchronization_logic'; + export const Synchronization: React.FC = () => { + const { contentSource } = useValues(SourceLogic); + const { updateSyncEnabled } = useActions(SynchronizationLogic({ contentSource })); + const { - contentSource: { isSyncConfigEnabled }, - } = useValues(SourceLogic); + isSyncConfigEnabled, + indexing: { enabled }, + } = contentSource; - const onChange = (checked: boolean) => `#TODO: ${checked}`; + const onChange = (checked: boolean) => updateSyncEnabled(checked); const syncToggle = ( onChange(e.target.checked)} /> diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/synchronization/synchronization_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/synchronization/synchronization_logic.test.ts index 50553d14934174..c51ef6cf2bf34f 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/synchronization/synchronization_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/synchronization/synchronization_logic.test.ts @@ -5,14 +5,22 @@ * 2.0. */ -import { LogicMounter, mockKibanaValues } from '../../../../../__mocks__/kea_logic'; +import { + LogicMounter, + mockFlashMessageHelpers, + mockHttpValues, + mockKibanaValues, +} from '../../../../../__mocks__/kea_logic'; +import { fullContentSources } from '../../../../__mocks__/content_sources.mock'; import { nextTick } from '@kbn/test/jest'; -const contentSource = { id: 'source123' }; +import { expectedAsyncError } from '../../../../../test_helpers'; + jest.mock('../../source_logic', () => ({ - SourceLogic: { values: { contentSource } }, + SourceLogic: { actions: { setContentSource: jest.fn() } }, })); +import { SourceLogic } from '../../source_logic'; jest.mock('../../../../app_logic', () => ({ AppLogic: { values: { isOrganization: true } }, @@ -21,17 +29,23 @@ jest.mock('../../../../app_logic', () => ({ import { SynchronizationLogic, emptyBlockedWindow } from './synchronization_logic'; describe('SynchronizationLogic', () => { + const { http } = mockHttpValues; + const { flashAPIErrors, flashSuccessToast } = mockFlashMessageHelpers; const { navigateToUrl } = mockKibanaValues; const { mount } = new LogicMounter(SynchronizationLogic); + const contentSource = fullContentSources[0]; const defaultValues = { navigatingBetweenTabs: false, + hasUnsavedObjectsAndAssetsChanges: false, + contentExtractionChecked: true, + thumbnailsChecked: true, blockedWindows: [], }; beforeEach(() => { jest.clearAllMocks(); - mount(); + mount({}, { contentSource }); }); it('has expected default values', () => { @@ -50,6 +64,18 @@ describe('SynchronizationLogic', () => { expect(SynchronizationLogic.values.blockedWindows).toEqual([emptyBlockedWindow]); }); + + it('setThumbnailsChecked', () => { + SynchronizationLogic.actions.setThumbnailsChecked(false); + + expect(SynchronizationLogic.values.thumbnailsChecked).toEqual(false); + }); + + it('setContentExtractionChecked', () => { + SynchronizationLogic.actions.setContentExtractionChecked(false); + + expect(SynchronizationLogic.values.contentExtractionChecked).toEqual(false); + }); }); describe('listeners', () => { @@ -63,7 +89,7 @@ describe('SynchronizationLogic', () => { await nextTick(); expect(setNavigatingBetweenTabsSpy).toHaveBeenCalledWith(true); - expect(navigateToUrl).toHaveBeenCalledWith('/sources/source123/synchronization/frequency'); + expect(navigateToUrl).toHaveBeenCalledWith('/sources/123/synchronization/frequency'); }); it('calls calls correct route for "blocked_time_windows"', async () => { @@ -71,8 +97,125 @@ describe('SynchronizationLogic', () => { await nextTick(); expect(navigateToUrl).toHaveBeenCalledWith( - '/sources/source123/synchronization/frequency/blocked_windows' + '/sources/123/synchronization/frequency/blocked_windows' + ); + }); + }); + + describe('updateSyncEnabled', () => { + it('calls API and sets values for false value', async () => { + const setContentSourceSpy = jest.spyOn(SourceLogic.actions, 'setContentSource'); + const promise = Promise.resolve(contentSource); + http.patch.mockReturnValue(promise); + SynchronizationLogic.actions.updateSyncEnabled(false); + + expect(http.patch).toHaveBeenCalledWith( + '/internal/workplace_search/org/sources/123/settings', + { + body: JSON.stringify({ + content_source: { + indexing: { enabled: false }, + }, + }), + } + ); + await promise; + expect(setContentSourceSpy).toHaveBeenCalledWith(contentSource); + expect(flashSuccessToast).toHaveBeenCalledWith('Source synchronization disabled.'); + }); + + it('calls API and sets values for true value', async () => { + const promise = Promise.resolve(contentSource); + http.patch.mockReturnValue(promise); + SynchronizationLogic.actions.updateSyncEnabled(true); + + expect(http.patch).toHaveBeenCalledWith( + '/internal/workplace_search/org/sources/123/settings', + { + body: JSON.stringify({ + content_source: { + indexing: { enabled: true }, + }, + }), + } + ); + await promise; + expect(flashSuccessToast).toHaveBeenCalledWith('Source synchronization enabled.'); + }); + + it('handles error', async () => { + const error = { + response: { + error: 'this is an error', + status: 400, + }, + }; + const promise = Promise.reject(error); + http.patch.mockReturnValue(promise); + SynchronizationLogic.actions.updateSyncEnabled(false); + await expectedAsyncError(promise); + + expect(flashAPIErrors).toHaveBeenCalledWith(error); + }); + }); + + describe('resetSyncSettings', () => { + it('calls methods', async () => { + const setThumbnailsCheckedSpy = jest.spyOn( + SynchronizationLogic.actions, + 'setThumbnailsChecked' + ); + const setContentExtractionCheckedSpy = jest.spyOn( + SynchronizationLogic.actions, + 'setContentExtractionChecked' ); + SynchronizationLogic.actions.resetSyncSettings(); + + expect(setThumbnailsCheckedSpy).toHaveBeenCalledWith(true); + expect(setContentExtractionCheckedSpy).toHaveBeenCalledWith(true); + }); + }); + + describe('updateSyncSettings', () => { + it('calls API and sets values', async () => { + const setContentSourceSpy = jest.spyOn(SourceLogic.actions, 'setContentSource'); + const promise = Promise.resolve(contentSource); + http.patch.mockReturnValue(promise); + SynchronizationLogic.actions.updateSyncSettings(); + + expect(http.patch).toHaveBeenCalledWith( + '/internal/workplace_search/org/sources/123/settings', + { + body: JSON.stringify({ + content_source: { + indexing: { + features: { + content_extraction: { enabled: true }, + thumbnails: { enabled: true }, + }, + }, + }, + }), + } + ); + await promise; + expect(setContentSourceSpy).toHaveBeenCalledWith(contentSource); + expect(flashSuccessToast).toHaveBeenCalledWith('Source synchronization settings updated.'); + }); + + it('handles error', async () => { + const error = { + response: { + error: 'this is an error', + status: 400, + }, + }; + const promise = Promise.reject(error); + http.patch.mockReturnValue(promise); + SynchronizationLogic.actions.updateSyncSettings(); + await expectedAsyncError(promise); + + expect(flashAPIErrors).toHaveBeenCalledWith(error); }); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/synchronization/synchronization_logic.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/synchronization/synchronization_logic.ts index 4f67f6471e6e13..4106ab70cf2011 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/synchronization/synchronization_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/synchronization/synchronization_logic.ts @@ -10,6 +10,8 @@ import moment from 'moment'; export type TabId = 'source_sync_frequency' | 'blocked_time_windows'; +import { flashAPIErrors, flashSuccessToast } from '../../../../../shared/flash_messages'; +import { HttpLogic } from '../../../../../shared/http'; import { KibanaLogic } from '../../../../../shared/kibana'; import { AppLogic } from '../../../../app_logic'; import { @@ -19,17 +21,30 @@ import { } from '../../../../routes'; import { BlockedWindow } from '../../../../types'; +import { + SYNC_ENABLED_MESSAGE, + SYNC_DISABLED_MESSAGE, + SYNC_SETTINGS_UPDATED_MESSAGE, +} from '../../constants'; import { SourceLogic } from '../../source_logic'; interface SynchronizationActions { setNavigatingBetweenTabs(navigatingBetweenTabs: boolean): boolean; handleSelectedTabChanged(tabId: TabId): TabId; addBlockedWindow(): void; + updateSyncSettings(): void; + resetSyncSettings(): void; + updateSyncEnabled(enabled: boolean): boolean; + setThumbnailsChecked(checked: boolean): boolean; + setContentExtractionChecked(checked: boolean): boolean; } interface SynchronizationValues { - hasUnsavedChanges: boolean; navigatingBetweenTabs: boolean; + hasUnsavedFrequencyChanges: boolean; + hasUnsavedObjectsAndAssetsChanges: boolean; + thumbnailsChecked: boolean; + contentExtractionChecked: boolean; blockedWindows: BlockedWindow[]; } @@ -43,12 +58,18 @@ export const emptyBlockedWindow: BlockedWindow = { export const SynchronizationLogic = kea< MakeLogicType >({ + path: ['enterprise_search', 'workplace_search', 'synchronization_logic'], actions: { setNavigatingBetweenTabs: (navigatingBetweenTabs: boolean) => navigatingBetweenTabs, handleSelectedTabChanged: (tabId: TabId) => tabId, + updateSyncEnabled: (enabled: boolean) => enabled, + setThumbnailsChecked: (checked: boolean) => checked, + setContentExtractionChecked: (checked: boolean) => checked, + updateSyncSettings: true, + resetSyncSettings: true, addBlockedWindow: true, }, - reducers: { + reducers: ({ props }) => ({ navigatingBetweenTabs: [ false, { @@ -61,11 +82,47 @@ export const SynchronizationLogic = kea< addBlockedWindow: (state, _) => [...state, emptyBlockedWindow], }, ], - }, - listeners: ({ actions }) => ({ + thumbnailsChecked: [ + props.contentSource.indexing.features.thumbnails.enabled, + { + setThumbnailsChecked: (_, thumbnailsChecked) => thumbnailsChecked, + }, + ], + contentExtractionChecked: [ + props.contentSource.indexing.features.contentExtraction.enabled, + { + setContentExtractionChecked: (_, contentExtractionChecked) => contentExtractionChecked, + }, + ], + }), + selectors: ({ selectors }) => ({ + hasUnsavedObjectsAndAssetsChanges: [ + () => [ + selectors.thumbnailsChecked, + selectors.contentExtractionChecked, + (_, props) => props.contentSource, + ], + (thumbnailsChecked, contentExtractionChecked, contentSource) => { + const { + indexing: { + features: { + thumbnails: { enabled: thumbnailsEnabled }, + contentExtraction: { enabled: contentExtractionEnabled }, + }, + }, + } = contentSource; + + return ( + thumbnailsChecked !== thumbnailsEnabled || + contentExtractionChecked !== contentExtractionEnabled + ); + }, + ], + }), + listeners: ({ actions, values, props }) => ({ handleSelectedTabChanged: async (tabId, breakpoint) => { const { isOrganization } = AppLogic.values; - const { id: sourceId } = SourceLogic.values.contentSource; + const { id: sourceId } = props.contentSource; const path = tabId === 'source_sync_frequency' ? getContentSourcePath(SYNC_FREQUENCY_PATH, sourceId, isOrganization) @@ -82,5 +139,51 @@ export const SynchronizationLogic = kea< KibanaLogic.values.navigateToUrl(path); actions.setNavigatingBetweenTabs(false); }, + updateSyncEnabled: async (enabled) => { + const { id: sourceId } = props.contentSource; + const route = `/internal/workplace_search/org/sources/${sourceId}/settings`; + const successMessage = enabled ? SYNC_ENABLED_MESSAGE : SYNC_DISABLED_MESSAGE; + + try { + const response = await HttpLogic.values.http.patch(route, { + body: JSON.stringify({ content_source: { indexing: { enabled } } }), + }); + + SourceLogic.actions.setContentSource(response); + flashSuccessToast(successMessage); + } catch (e) { + flashAPIErrors(e); + } + }, + resetSyncSettings: () => { + actions.setThumbnailsChecked(props.contentSource.indexing.features.thumbnails.enabled); + actions.setContentExtractionChecked( + props.contentSource.indexing.features.contentExtraction.enabled + ); + }, + updateSyncSettings: async () => { + const { id: sourceId } = props.contentSource; + const route = `/internal/workplace_search/org/sources/${sourceId}/settings`; + + try { + const response = await HttpLogic.values.http.patch(route, { + body: JSON.stringify({ + content_source: { + indexing: { + features: { + content_extraction: { enabled: values.contentExtractionChecked }, + thumbnails: { enabled: values.thumbnailsChecked }, + }, + }, + }, + }), + }); + + SourceLogic.actions.setContentSource(response); + flashSuccessToast(SYNC_SETTINGS_UPDATED_MESSAGE); + } catch (e) { + flashAPIErrors(e); + } + }, }), }); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/constants.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/constants.ts index ae55a970a4f9f8..4e46100b591b91 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/constants.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/constants.ts @@ -306,20 +306,6 @@ export const SOURCE_CONFIG_TITLE = i18n.translate( } ); -export const SYNC_MANAGEMENT_TITLE = i18n.translate( - 'xpack.enterpriseSearch.workplaceSearch.contentSources.syncManagementTitle', - { - defaultMessage: 'Sync management', - } -); - -export const SYNC_MANAGEMENT_DESCRIPTION = i18n.translate( - 'xpack.enterpriseSearch.workplaceSearch.contentSources.syncManagementDescription', - { - defaultMessage: 'Enable and disable extraction of specific content for this source.', - } -); - export const SYNC_MANAGEMENT_SYNCHRONIZE_LABEL = i18n.translate( 'xpack.enterpriseSearch.workplaceSearch.contentSources.syncManagementSynchronizeLabel', { @@ -344,7 +330,7 @@ export const SYNC_MANAGEMENT_THUMBNAILS_GLOBAL_CONFIG_LABEL = i18n.translate( export const SYNC_MANAGEMENT_CONTENT_EXTRACTION_LABEL = i18n.translate( 'xpack.enterpriseSearch.workplaceSearch.contentSources.syncManagementContentExtractionLabel', { - defaultMessage: 'Sync all text and content', + defaultMessage: 'Sync full-text from files', } ); @@ -565,6 +551,13 @@ export const SOURCE_OBJECTS_AND_ASSETS_DESCRIPTION = i18n.translate( } ); +export const SOURCE_OBJECTS_AND_ASSETS_LABEL = i18n.translate( + 'xpack.enterpriseSearch.workplaceSearch.sources.sourceObjectsAndAssetsLabel', + { + defaultMessage: 'Object and details to include in search results', + } +); + export const SOURCE_SYNCRONIZATION_TOGGLE_LABEL = i18n.translate( 'xpack.enterpriseSearch.workplaceSearch.sources.sourceSyncronizationToggleLabel', { @@ -711,3 +704,38 @@ export const BLOCKED_EMPTY_STATE_DESCRIPTION = i18n.translate( defaultMessage: 'Add a blocked time window to only perform syncs at the right time.', } ); + +export const SYNC_ENABLED_MESSAGE = i18n.translate( + 'xpack.enterpriseSearch.workplaceSearch.sources.syncEnabledMessage', + { + defaultMessage: 'Source synchronization enabled.', + } +); + +export const SYNC_DISABLED_MESSAGE = i18n.translate( + 'xpack.enterpriseSearch.workplaceSearch.sources.syncDisabledMessage', + { + defaultMessage: 'Source synchronization disabled.', + } +); + +export const SYNC_SETTINGS_UPDATED_MESSAGE = i18n.translate( + 'xpack.enterpriseSearch.workplaceSearch.sources.syncSettingsUpdatedMessage', + { + defaultMessage: 'Source synchronization settings updated.', + } +); + +export const SYNC_UNSAVED_CHANGES_MESSAGE = i18n.translate( + 'xpack.enterpriseSearch.workplaceSearch.sources.syncUnsavedChangesMessage', + { + defaultMessage: 'Your changes have not been saved. Are you sure you want to leave?', + } +); + +export const NEXT_SYNC_RUNNING_MESSAGE = i18n.translate( + 'xpack.enterpriseSearch.workplaceSearch.sources.nextSyncRunningMessage', + { + defaultMessage: 'as soon as the currently running job finishes', + } +); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/source_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/source_logic.test.ts index 0d8d5684c4a4cf..1fb4477cea5c02 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/source_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/source_logic.test.ts @@ -58,8 +58,8 @@ describe('SourceLogic', () => { }); describe('actions', () => { - it('onInitializeSource', () => { - SourceLogic.actions.onInitializeSource(contentSource); + it('setContentSource', () => { + SourceLogic.actions.setContentSource(contentSource); expect(SourceLogic.values.contentSource).toEqual(contentSource); expect(SourceLogic.values.dataLoading).toEqual(false); @@ -67,7 +67,7 @@ describe('SourceLogic', () => { it('onUpdateSourceName', () => { const NAME = 'foo'; - SourceLogic.actions.onInitializeSource(contentSource); + SourceLogic.actions.setContentSource(contentSource); SourceLogic.actions.onUpdateSourceName(NAME); expect(SourceLogic.values.contentSource).toEqual({ @@ -88,7 +88,7 @@ describe('SourceLogic', () => { it('setContentFilterValue', () => { const VALUE = 'bar'; SourceLogic.actions.setSearchResults(searchServerResponse); - SourceLogic.actions.onInitializeSource(contentSource); + SourceLogic.actions.setContentSource(contentSource); SourceLogic.actions.setContentFilterValue(VALUE); expect(SourceLogic.values.contentMeta).toEqual({ @@ -127,7 +127,7 @@ describe('SourceLogic', () => { describe('listeners', () => { describe('initializeSource', () => { it('calls API and sets values (org)', async () => { - const onInitializeSourceSpy = jest.spyOn(SourceLogic.actions, 'onInitializeSource'); + const onInitializeSourceSpy = jest.spyOn(SourceLogic.actions, 'setContentSource'); const promise = Promise.resolve(contentSource); http.get.mockReturnValue(promise); SourceLogic.actions.initializeSource(contentSource.id); @@ -140,7 +140,7 @@ describe('SourceLogic', () => { it('calls API and sets values (account)', async () => { AppLogic.values.isOrganization = false; - const onInitializeSourceSpy = jest.spyOn(SourceLogic.actions, 'onInitializeSource'); + const onInitializeSourceSpy = jest.spyOn(SourceLogic.actions, 'setContentSource'); const promise = Promise.resolve(contentSource); http.get.mockReturnValue(promise); SourceLogic.actions.initializeSource(contentSource.id); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/source_logic.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/source_logic.ts index c31eacda695151..d10400bc5ba2d2 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/source_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/source_logic.ts @@ -23,7 +23,7 @@ import { PRIVATE_SOURCES_PATH, SOURCES_PATH, getSourcesPath } from '../../routes import { ContentSourceFullData, Meta, DocumentSummaryItem, SourceContentItem } from '../../types'; export interface SourceActions { - onInitializeSource(contentSource: ContentSourceFullData): ContentSourceFullData; + setContentSource(contentSource: ContentSourceFullData): ContentSourceFullData; onUpdateSourceName(name: string): string; setSearchResults(searchResultsResponse: SearchResultsResponse): SearchResultsResponse; initializeFederatedSummary(sourceId: string): { sourceId: string }; @@ -73,7 +73,7 @@ interface SourceUpdatePayload { export const SourceLogic = kea>({ path: ['enterprise_search', 'workplace_search', 'source_logic'], actions: { - onInitializeSource: (contentSource: ContentSourceFullData) => contentSource, + setContentSource: (contentSource: ContentSourceFullData) => contentSource, onUpdateSourceName: (name: string) => name, onUpdateSummary: (summary: object[]) => summary, setSearchResults: (searchResultsResponse: SearchResultsResponse) => searchResultsResponse, @@ -93,7 +93,7 @@ export const SourceLogic = kea>({ contentSource: [ {} as ContentSourceFullData, { - onInitializeSource: (_, contentSource) => contentSource, + setContentSource: (_, contentSource) => contentSource, onUpdateSourceName: (contentSource, name) => ({ ...contentSource, name, @@ -108,7 +108,7 @@ export const SourceLogic = kea>({ dataLoading: [ true, { - onInitializeSource: () => false, + setContentSource: () => false, resetSourceState: () => true, }, ], @@ -158,7 +158,7 @@ export const SourceLogic = kea>({ try { const response = await HttpLogic.values.http.get(route); - actions.onInitializeSource(response); + actions.setContentSource(response); if (response.isFederatedSource) { actions.initializeFederatedSummary(sourceId); } diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index cca07673ad6d3a..408407a8b7ba4f 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -10121,11 +10121,9 @@ "xpack.enterpriseSearch.workplaceSearch.contentSources.displaySettings.unsaved.message": "表示設定は保存されていません。終了してよろしいですか?", "xpack.enterpriseSearch.workplaceSearch.contentSources.displaySettings.visibleFields.title": "表示フィールド", "xpack.enterpriseSearch.workplaceSearch.contentSources.syncManagementContentExtractionLabel": "すべてのテキストとコンテンツを同期", - "xpack.enterpriseSearch.workplaceSearch.contentSources.syncManagementDescription": "このソースの特定のコンテンツの抽出を有効および無効にします。", "xpack.enterpriseSearch.workplaceSearch.contentSources.syncManagementGlobalConfigLabel": "サムネイルを同期 - グローバル構成レベルでは無効", "xpack.enterpriseSearch.workplaceSearch.contentSources.syncManagementSynchronizeLabel": "このソースを同期", "xpack.enterpriseSearch.workplaceSearch.contentSources.syncManagementThumbnailsLabel": "サムネイルを同期", - "xpack.enterpriseSearch.workplaceSearch.contentSources.syncManagementTitle": "同期管理", "xpack.enterpriseSearch.workplaceSearch.copyText": "コピー", "xpack.enterpriseSearch.workplaceSearch.credentials.description": "クライアントで次の資格情報を使用して、認証サーバーからアクセストークンを要求します。", "xpack.enterpriseSearch.workplaceSearch.credentials.title": "資格情報", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index e883b0f728774b..29d51a4264b400 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -10223,11 +10223,9 @@ "xpack.enterpriseSearch.workplaceSearch.contentSources.displaySettings.unsaved.message": "您的显示设置尚未保存。是否确定要离开?", "xpack.enterpriseSearch.workplaceSearch.contentSources.displaySettings.visibleFields.title": "可见的字段", "xpack.enterpriseSearch.workplaceSearch.contentSources.syncManagementContentExtractionLabel": "同步所有文本和内容", - "xpack.enterpriseSearch.workplaceSearch.contentSources.syncManagementDescription": "为此源启用和禁用特定内容的提取。", "xpack.enterpriseSearch.workplaceSearch.contentSources.syncManagementGlobalConfigLabel": "同步缩略图 - 已在全局配置级别禁用", "xpack.enterpriseSearch.workplaceSearch.contentSources.syncManagementSynchronizeLabel": "同步此源", "xpack.enterpriseSearch.workplaceSearch.contentSources.syncManagementThumbnailsLabel": "同步缩略图", - "xpack.enterpriseSearch.workplaceSearch.contentSources.syncManagementTitle": "同步管理", "xpack.enterpriseSearch.workplaceSearch.copyText": "复制", "xpack.enterpriseSearch.workplaceSearch.credentials.description": "在您的客户端中使用以下凭据从我们的身份验证服务器请求访问令牌。", "xpack.enterpriseSearch.workplaceSearch.credentials.title": "凭据",