diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/__mocks__/content_sources.mock.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/__mocks__/content_sources.mock.ts
index fab241163f470..48cbf4ba00d87 100644
--- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/__mocks__/content_sources.mock.ts
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/__mocks__/content_sources.mock.ts
@@ -54,6 +54,7 @@ const defaultIndexing = {
incremental: 'PT2H',
delete: 'PT10M',
permissions: 'PT3H',
+ blockedWindows: [],
estimates: {
full: {
nextStart: '2021-09-30T15:37:38+00:00',
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/routes.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/routes.ts
index 691b52c9f51e4..ec515eed91179 100644
--- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/routes.ts
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/routes.ts
@@ -45,10 +45,9 @@ export const CUSTOM_SOURCE_DOCS_URL = `${DOCS_PREFIX}/workplace-search-custom-ap
export const CUSTOM_API_DOCS_URL = `${DOCS_PREFIX}/workplace-search-custom-sources-api.html`;
export const CUSTOM_API_DOCUMENT_PERMISSIONS_DOCS_URL = `${CUSTOM_SOURCE_DOCS_URL}#custom-api-source-document-level-access-control`;
export const ENT_SEARCH_LICENSE_MANAGEMENT = `${docLinks.enterpriseSearchBase}/license-management.html`;
-export const SYNCHRONIZATION_DOCS_URL = '#TODO';
-export const DIFFERENT_SYNC_TYPES_DOCS_URL = '#TODO';
-export const SYNC_BEST_PRACTICES_DOCS_URL = '#TODO';
-export const OBJECTS_AND_ASSETS_DOCS_URL = '#TODO';
+export const SYNCHRONIZATION_DOCS_URL = `${DOCS_PREFIX}}/workplace-search-customizing-indexing-rules.html#workplace-search-customizing-indexing-rules`;
+export const DIFFERENT_SYNC_TYPES_DOCS_URL = `${DOCS_PREFIX}}/workplace-search-customizing-indexing-rules.html#_indexing_schedule`;
+export const OBJECTS_AND_ASSETS_DOCS_URL = `${DOCS_PREFIX}}/workplace-search-customizing-indexing-rules.html#workplace-search-customizing-indexing-rules`;
export const PERSONAL_PATH = '/p';
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/types.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/types.ts
index 72bcf850fbcd9..ab45c54cc5c57 100644
--- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/types.ts
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/types.ts
@@ -5,8 +5,6 @@
* 2.0.
*/
-import { Moment } from 'moment';
-
import { RoleMapping } from '../shared/types';
export * from '../../../common/types/workplace_search';
@@ -166,8 +164,8 @@ export type DayOfWeek = typeof DAYS_OF_WEEK_VALUES[number];
export interface BlockedWindow {
jobType: SyncJobType;
day: DayOfWeek | 'all';
- start: Moment;
- end: Moment;
+ start: string;
+ end: string;
}
export interface IndexingConfig {
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/synchronization/__mocks__/syncronization.mock.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/synchronization/__mocks__/synchronization.mock.ts
similarity index 75%
rename from x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/synchronization/__mocks__/syncronization.mock.ts
rename to x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/synchronization/__mocks__/synchronization.mock.ts
index a71be55339f48..af6a0552fc27b 100644
--- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/synchronization/__mocks__/syncronization.mock.ts
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/synchronization/__mocks__/synchronization.mock.ts
@@ -5,13 +5,11 @@
* 2.0.
*/
-import moment from 'moment';
-
import { SyncJobType, DayOfWeek } from '../../../../../types';
export const blockedWindow = {
jobType: 'incremental' as SyncJobType,
day: 'sunday' as DayOfWeek,
- start: moment().set('hour', 11).set('minutes', 0),
- end: moment().set('hour', 13).set('minutes', 0),
+ start: '11:00:00Z',
+ end: '13:00:00Z',
};
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/synchronization/blocked_window_item.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/synchronization/blocked_window_item.test.tsx
index 703b1f9d8c5fe..f3c01b8d94d37 100644
--- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/synchronization/blocked_window_item.test.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/synchronization/blocked_window_item.test.tsx
@@ -5,18 +5,44 @@
* 2.0.
*/
-import { blockedWindow } from './__mocks__/syncronization.mock';
+import '../../../../../__mocks__/shallow_useeffect.mock';
+import { setMockActions, setMockValues } from '../../../../../__mocks__/kea_logic';
+import { fullContentSources } from '../../../../__mocks__/content_sources.mock';
+import { blockedWindow } from './__mocks__/synchronization.mock';
import React from 'react';
import { shallow } from 'enzyme';
+import moment from 'moment';
-import { EuiDatePickerRange, EuiSelect, EuiSuperSelect } from '@elastic/eui';
+import {
+ EuiButton,
+ EuiDatePicker,
+ EuiDatePickerRange,
+ EuiSelect,
+ EuiSuperSelect,
+} from '@elastic/eui';
import { BlockedWindowItem } from './blocked_window_item';
describe('BlockedWindowItem', () => {
- const props = { blockedWindow };
+ const removeBlockedWindow = jest.fn();
+ const setBlockedTimeWindow = jest.fn();
+ const mockActions = {
+ removeBlockedWindow,
+ setBlockedTimeWindow,
+ };
+ const mockValues = {
+ contentSource: fullContentSources[0],
+ };
+
+ beforeEach(() => {
+ setMockActions(mockActions);
+ setMockValues(mockValues);
+ });
+
+ const props = { blockedWindow, index: 0 };
+
it('renders', () => {
const wrapper = shallow();
@@ -24,4 +50,47 @@ describe('BlockedWindowItem', () => {
expect(wrapper.find(EuiSuperSelect)).toHaveLength(1);
expect(wrapper.find(EuiDatePickerRange)).toHaveLength(1);
});
+
+ it('handles remove button click', () => {
+ const wrapper = shallow();
+ wrapper.find(EuiButton).simulate('click');
+
+ expect(removeBlockedWindow).toHaveBeenCalledWith(0);
+ });
+
+ it('handles "jobType" select change', () => {
+ const wrapper = shallow();
+ wrapper.find(EuiSuperSelect).simulate('change', 'delete');
+
+ expect(setBlockedTimeWindow).toHaveBeenCalledWith(0, 'jobType', 'delete');
+ });
+
+ it('handles "day" select change', () => {
+ const wrapper = shallow();
+ wrapper.find(EuiSelect).simulate('change', { target: { value: 'tuesday' } });
+
+ expect(setBlockedTimeWindow).toHaveBeenCalledWith(0, 'day', 'tuesday');
+ });
+
+ it('handles "start" time change', () => {
+ const wrapper = shallow();
+ const dayRange = wrapper.find(EuiDatePickerRange).dive();
+ dayRange
+ .find(EuiDatePicker)
+ .first()
+ .simulate('change', moment().utc().set({ hour: 10, minute: 0, seconds: 0 }));
+
+ expect(setBlockedTimeWindow).toHaveBeenCalledWith(0, 'start', '10:00:00Z');
+ });
+
+ it('handles "end" time change', () => {
+ const wrapper = shallow();
+ const dayRange = wrapper.find(EuiDatePickerRange).dive();
+ dayRange
+ .find(EuiDatePicker)
+ .last()
+ .simulate('change', moment().utc().set({ hour: 12, minute: 0, seconds: 0 }));
+
+ expect(setBlockedTimeWindow).toHaveBeenCalledWith(0, 'end', '12:00:00Z');
+ });
});
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/synchronization/blocked_window_item.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/synchronization/blocked_window_item.tsx
index 5aec23b5faaea..272efc6fc3c50 100644
--- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/synchronization/blocked_window_item.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/synchronization/blocked_window_item.tsx
@@ -7,6 +7,7 @@
import React from 'react';
+import { useActions, useValues } from 'kea';
import moment from 'moment';
import {
@@ -40,8 +41,13 @@ import {
UTC_TITLE,
} from '../../constants';
+import { SourceLogic } from '../../source_logic';
+
+import { SynchronizationLogic } from './synchronization_logic';
+
interface Props {
blockedWindow: BlockedWindow;
+ index: number;
}
const syncOptions = [
@@ -66,7 +72,7 @@ const syncOptions = [
),
},
{
- value: 'deletion',
+ value: 'delete',
inputDisplay: DELETION_SYNC_LABEL,
dropdownDisplay: (
<>
@@ -93,10 +99,11 @@ const daySelectOptions = DAYS_OF_WEEK_VALUES.map((day) => ({
})) as EuiSelectOption[];
daySelectOptions.push({ text: ALL_DAYS_LABEL, value: 'all' });
-export const BlockedWindowItem: React.FC = ({ blockedWindow }) => {
- const handleSyncTypeChange = () => '#TODO';
- const handleStartDateChange = () => '#TODO';
- const handleEndDateChange = () => '#TODO';
+export const BlockedWindowItem: React.FC = ({ blockedWindow, index }) => {
+ const { contentSource } = useValues(SourceLogic);
+ const { removeBlockedWindow, setBlockedTimeWindow } = useActions(
+ SynchronizationLogic({ contentSource })
+ );
return (
<>
@@ -109,7 +116,7 @@ export const BlockedWindowItem: React.FC = ({ blockedWindow }) => {
setBlockedTimeWindow(index, 'jobType', value)}
itemClassName="blockedWindowSelectItem"
popoverClassName="blockedWindowSelectPopover"
/>
@@ -118,7 +125,11 @@ export const BlockedWindowItem: React.FC = ({ blockedWindow }) => {
{ON_LABEL}
-
+ setBlockedTimeWindow(index, 'day', e.target.value)}
+ options={daySelectOptions}
+ />
{BETWEEN_LABEL}
@@ -129,8 +140,11 @@ export const BlockedWindowItem: React.FC = ({ blockedWindow }) => {
+ value &&
+ setBlockedTimeWindow(index, 'start', `${value.utc().format('HH:mm:ss')}Z`)
+ }
dateFormat="h:mm A"
timeFormat="h:mm A"
/>
@@ -139,8 +153,10 @@ export const BlockedWindowItem: React.FC = ({ blockedWindow }) => {
+ value && setBlockedTimeWindow(index, 'end', `${value.utc().format('HH:mm:ss')}Z`)
+ }
dateFormat="h:mm A"
timeFormat="h:mm A"
/>
@@ -163,7 +179,7 @@ export const BlockedWindowItem: React.FC = ({ blockedWindow }) => {
/>
-
+ removeBlockedWindow(index)}>
{REMOVE_BUTTON}
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 7cada1d39fb6e..44973cef966ee 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
@@ -8,7 +8,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 { blockedWindow } from './__mocks__/synchronization.mock';
import React from 'react';
@@ -24,9 +24,11 @@ describe('BlockedWindows', () => {
const mockActions = {
addBlockedWindow,
};
+ const contentSource = { ...fullContentSources[0] };
+ contentSource.indexing.schedule.blockedWindows = [blockedWindow] as any;
const mockValues = {
- blockedWindows: [blockedWindow],
- contentSource: fullContentSources[0],
+ contentSource,
+ schedule: contentSource.indexing.schedule,
};
beforeEach(() => {
@@ -41,7 +43,7 @@ describe('BlockedWindows', () => {
});
it('renders empty state', () => {
- setMockValues({ blockedWindows: [] });
+ setMockValues({ schedule: { blockedWindows: [] } });
const wrapper = shallow();
expect(wrapper.find(EuiEmptyPrompt)).toHaveLength(1);
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 f0227f76d4aa5..488346c2d6e30 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
@@ -20,10 +20,12 @@ import { SynchronizationLogic } from './synchronization_logic';
export const BlockedWindows: React.FC = () => {
const { contentSource } = useValues(SourceLogic);
- const { blockedWindows } = useValues(SynchronizationLogic({ contentSource }));
+ const {
+ schedule: { blockedWindows },
+ } = useValues(SynchronizationLogic({ contentSource }));
const { addBlockedWindow } = useActions(SynchronizationLogic({ contentSource }));
- const hasBlockedWindows = blockedWindows.length > 0;
+ const hasBlockedWindows = blockedWindows && blockedWindows.length > 0;
const emptyState = (
<>
@@ -43,8 +45,8 @@ export const BlockedWindows: React.FC = () => {
const blockedWindowItems = (
<>
- {blockedWindows.map((blockedWindow, i) => (
-
+ {blockedWindows?.map((blockedWindow, i) => (
+
))}
{ADD_LABEL}
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 2ada5b64be889..a682e10269e6c 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
@@ -24,13 +24,12 @@ import { SAVE_BUTTON_LABEL } from '../../../../../shared/constants';
import { UnsavedChangesPrompt } from '../../../../../shared/unsaved_changes_prompt';
import { ViewContentHeader } from '../../../../components/shared/view_content_header';
import { NAV, RESET_BUTTON } from '../../../../constants';
-import { DIFFERENT_SYNC_TYPES_DOCS_URL, SYNC_BEST_PRACTICES_DOCS_URL } from '../../../../routes';
+import { DIFFERENT_SYNC_TYPES_DOCS_URL } from '../../../../routes';
import {
SOURCE_FREQUENCY_DESCRIPTION,
SOURCE_SYNC_FREQUENCY_TITLE,
BLOCKED_TIME_WINDOWS_TITLE,
- DIFFERENT_SYNC_TYPES_LINK_LABEL,
- SYNC_BEST_PRACTICES_LINK_LABEL,
+ SYNC_FREQUENCY_LINK_LABEL,
SYNC_UNSAVED_CHANGES_MESSAGE,
} from '../../constants';
import { SourceLogic } from '../../source_logic';
@@ -46,7 +45,9 @@ interface FrequencyProps {
export const Frequency: React.FC = ({ tabId }) => {
const { contentSource } = useValues(SourceLogic);
- const { hasUnsavedFrequencyChanges } = useValues(SynchronizationLogic({ contentSource }));
+ const { hasUnsavedFrequencyChanges, navigatingBetweenTabs } = useValues(
+ SynchronizationLogic({ contentSource })
+ );
const { handleSelectedTabChanged, resetSyncSettings, updateFrequencySettings } = useActions(
SynchronizationLogic({ contentSource })
);
@@ -87,12 +88,7 @@ export const Frequency: React.FC = ({ tabId }) => {
- {DIFFERENT_SYNC_TYPES_LINK_LABEL}
-
-
-
-
- {SYNC_BEST_PRACTICES_LINK_LABEL}
+ {SYNC_FREQUENCY_LINK_LABEL}
@@ -108,7 +104,7 @@ export const Frequency: React.FC = ({ tabId }) => {
isLoading={false}
>
{
action={actions}
/>
- {SYNC_OBJECTS_TYPES_LINK_LABEL}
+ {OBJECTS_AND_ASSETS_LINK_LABEL}
{SOURCE_OBJECTS_AND_ASSETS_LABEL}
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 21c44225615ea..e88d4d251fa54 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
@@ -15,11 +15,11 @@ import { ViewContentHeader } from '../../../../components/shared/view_content_he
import { NAV } from '../../../../constants';
import { SYNCHRONIZATION_DOCS_URL } from '../../../../routes';
import {
- SOURCE_SYNCRONIZATION_DESCRIPTION,
+ SOURCE_SYNCHRONIZATION_DESCRIPTION,
SYNCHRONIZATION_DISABLED_TITLE,
SYNCHRONIZATION_DISABLED_DESCRIPTION,
- SOURCE_SYNCRONIZATION_TOGGLE_LABEL,
- SOURCE_SYNCRONIZATION_TOGGLE_DESCRIPTION,
+ SOURCE_SYNCHRONIZATION_TOGGLE_LABEL,
+ SOURCE_SYNCHRONIZATION_TOGGLE_DESCRIPTION,
SYNCHRONIZATION_LINK_LABEL,
} from '../../constants';
import { SourceLogic } from '../../source_logic';
@@ -40,13 +40,13 @@ export const Synchronization: React.FC = () => {
const syncToggle = (
onChange(e.target.checked)}
/>
- {SOURCE_SYNCRONIZATION_TOGGLE_DESCRIPTION}
+ {SOURCE_SYNCHRONIZATION_TOGGLE_DESCRIPTION}
);
@@ -65,7 +65,7 @@ export const Synchronization: React.FC = () => {
>
{SYNCHRONIZATION_LINK_LABEL}
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 cfd64118c3358..25fb256e85f01 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
@@ -38,6 +38,16 @@ describe('SynchronizationLogic', () => {
const { navigateToUrl } = mockKibanaValues;
const { mount } = new LogicMounter(SynchronizationLogic);
const contentSource = fullContentSources[0];
+ const sourceWithNoBlockedWindows = {
+ ...contentSource,
+ indexing: {
+ ...contentSource.indexing,
+ schedule: {
+ ...contentSource.indexing.schedule,
+ blockedWindows: undefined,
+ },
+ },
+ };
const defaultValues = {
navigatingBetweenTabs: false,
@@ -45,7 +55,6 @@ describe('SynchronizationLogic', () => {
hasUnsavedFrequencyChanges: false,
contentExtractionChecked: true,
thumbnailsChecked: true,
- blockedWindows: [],
schedule: contentSource.indexing.schedule,
cachedSchedule: contentSource.indexing.schedule,
};
@@ -66,10 +75,23 @@ describe('SynchronizationLogic', () => {
expect(SynchronizationLogic.values.navigatingBetweenTabs).toEqual(true);
});
- it('addBlockedWindow', () => {
- SynchronizationLogic.actions.addBlockedWindow();
+ describe('addBlockedWindow', () => {
+ it('creates and populates empty array when undefined', () => {
+ mount({}, { contentSource: sourceWithNoBlockedWindows });
+ SynchronizationLogic.actions.addBlockedWindow();
+
+ expect(SynchronizationLogic.values.schedule.blockedWindows).toEqual([emptyBlockedWindow]);
+ });
- expect(SynchronizationLogic.values.blockedWindows).toEqual([emptyBlockedWindow]);
+ it('adds item when list has items', () => {
+ SynchronizationLogic.actions.addBlockedWindow();
+ SynchronizationLogic.actions.addBlockedWindow();
+
+ expect(SynchronizationLogic.values.schedule.blockedWindows).toEqual([
+ emptyBlockedWindow,
+ emptyBlockedWindow,
+ ]);
+ });
});
it('setThumbnailsChecked', () => {
@@ -112,6 +134,55 @@ describe('SynchronizationLogic', () => {
expect(SynchronizationLogic.values.schedule.full).toEqual('P1DT30M');
});
});
+
+ describe('removeBlockedWindow', () => {
+ it('removes window', () => {
+ SynchronizationLogic.actions.addBlockedWindow();
+ SynchronizationLogic.actions.addBlockedWindow();
+ SynchronizationLogic.actions.removeBlockedWindow(0);
+
+ expect(SynchronizationLogic.values.schedule.blockedWindows).toEqual([emptyBlockedWindow]);
+ });
+
+ it('returns "undefined" when last window removed', () => {
+ SynchronizationLogic.actions.addBlockedWindow();
+ SynchronizationLogic.actions.removeBlockedWindow(0);
+
+ expect(SynchronizationLogic.values.schedule.blockedWindows).toBeUndefined();
+ });
+ });
+ });
+
+ describe('setBlockedTimeWindow', () => {
+ it('sets "jobType"', () => {
+ SynchronizationLogic.actions.addBlockedWindow();
+ SynchronizationLogic.actions.setBlockedTimeWindow(0, 'jobType', 'incremental');
+
+ expect(SynchronizationLogic.values.schedule.blockedWindows![0].jobType).toEqual(
+ 'incremental'
+ );
+ });
+
+ it('sets "day"', () => {
+ SynchronizationLogic.actions.addBlockedWindow();
+ SynchronizationLogic.actions.setBlockedTimeWindow(0, 'day', 'tuesday');
+
+ expect(SynchronizationLogic.values.schedule.blockedWindows![0].day).toEqual('tuesday');
+ });
+
+ it('sets "start"', () => {
+ SynchronizationLogic.actions.addBlockedWindow();
+ SynchronizationLogic.actions.setBlockedTimeWindow(0, 'start', '9:00:00Z');
+
+ expect(SynchronizationLogic.values.schedule.blockedWindows![0].start).toEqual('9:00:00Z');
+ });
+
+ it('sets "end"', () => {
+ SynchronizationLogic.actions.addBlockedWindow();
+ SynchronizationLogic.actions.setBlockedTimeWindow(0, 'end', '11:00:00Z');
+
+ expect(SynchronizationLogic.values.schedule.blockedWindows![0].end).toEqual('11:00:00Z');
+ });
});
describe('listeners', () => {
@@ -177,6 +248,35 @@ describe('SynchronizationLogic', () => {
describe('updateFrequencySettings', () => {
it('calls updateServerSettings method', async () => {
+ SynchronizationLogic.actions.addBlockedWindow();
+ const updateServerSettingsSpy = jest.spyOn(
+ SynchronizationLogic.actions,
+ 'updateServerSettings'
+ );
+ SynchronizationLogic.actions.updateFrequencySettings();
+
+ expect(updateServerSettingsSpy).toHaveBeenCalledWith({
+ content_source: {
+ indexing: {
+ schedule: {
+ full: 'P1D',
+ incremental: 'PT2H',
+ delete: 'PT10M',
+ blocked_windows: [
+ {
+ day: 'monday',
+ end: '13:00:00Z',
+ job_type: 'full',
+ start: '11:00:00Z',
+ },
+ ],
+ },
+ },
+ },
+ });
+ });
+
+ it('handles case where blockedWindows undefined', async () => {
const updateServerSettingsSpy = jest.spyOn(
SynchronizationLogic.actions,
'updateServerSettings'
@@ -190,6 +290,7 @@ describe('SynchronizationLogic', () => {
full: 'P1D',
incremental: 'PT2H',
delete: 'PT10M',
+ blocked_windows: [],
},
},
},
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 95dbb8c75fce4..87a55f0e7dd3a 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
@@ -20,11 +20,19 @@ import {
BLOCKED_TIME_WINDOWS_PATH,
getContentSourcePath,
} from '../../../../routes';
-import { BlockedWindow, IndexingSchedule, SyncJobType, TimeUnit } from '../../../../types';
+import {
+ BlockedWindow,
+ DayOfWeek,
+ IndexingSchedule,
+ SyncJobType,
+ TimeUnit,
+} from '../../../../types';
import { SYNC_SETTINGS_UPDATED_MESSAGE } from '../../constants';
import { SourceLogic } from '../../source_logic';
+type BlockedWindowPropType = 'jobType' | 'day' | 'start' | 'end';
+
interface ServerBlockedWindow {
job_type: string;
day: string;
@@ -55,6 +63,7 @@ interface SynchronizationActions {
setNavigatingBetweenTabs(navigatingBetweenTabs: boolean): boolean;
handleSelectedTabChanged(tabId: TabId): TabId;
addBlockedWindow(): void;
+ removeBlockedWindow(index: number): number;
updateFrequencySettings(): void;
updateObjectsAndAssetsSettings(): void;
resetSyncSettings(): void;
@@ -65,6 +74,15 @@ interface SynchronizationActions {
value: string,
unit: TimeUnit
): { type: SyncJobType; value: number; unit: TimeUnit };
+ setBlockedTimeWindow(
+ index: number,
+ prop: BlockedWindowPropType,
+ value: string
+ ): {
+ index: number;
+ prop: BlockedWindowPropType;
+ value: string;
+ };
setContentExtractionChecked(checked: boolean): boolean;
setServerSchedule(schedule: IndexingSchedule): IndexingSchedule;
updateServerSettings(body: ServerSyncSettingsBody): ServerSyncSettingsBody;
@@ -76,7 +94,6 @@ interface SynchronizationValues {
hasUnsavedObjectsAndAssetsChanges: boolean;
thumbnailsChecked: boolean;
contentExtractionChecked: boolean;
- blockedWindows: BlockedWindow[];
cachedSchedule: IndexingSchedule;
schedule: IndexingSchedule;
}
@@ -84,8 +101,12 @@ interface SynchronizationValues {
export const emptyBlockedWindow: BlockedWindow = {
jobType: 'full',
day: 'monday',
- start: moment().set('hour', 11).set('minutes', 0),
- end: moment().set('hour', 13).set('minutes', 0),
+ start: '11:00:00Z',
+ end: '13:00:00Z',
+};
+
+type BlockedWindowMap = {
+ [prop in keyof BlockedWindow]: SyncJobType | DayOfWeek | 'all' | string;
};
export const SynchronizationLogic = kea<
@@ -102,9 +123,15 @@ export const SynchronizationLogic = kea<
value,
unit,
}),
+ setBlockedTimeWindow: (index: number, prop: BlockedWindowPropType, value: string) => ({
+ index,
+ prop,
+ value,
+ }),
setContentExtractionChecked: (checked: boolean) => checked,
updateServerSettings: (body: ServerSyncSettingsBody) => body,
setServerSchedule: (schedule: IndexingSchedule) => schedule,
+ removeBlockedWindow: (index: number) => index,
updateFrequencySettings: true,
updateObjectsAndAssetsSettings: true,
resetSyncSettings: true,
@@ -117,12 +144,6 @@ export const SynchronizationLogic = kea<
setNavigatingBetweenTabs: (_, navigatingBetweenTabs) => navigatingBetweenTabs,
},
],
- blockedWindows: [
- props.contentSource.indexing.schedule.blockedWindows || [],
- {
- addBlockedWindow: (state, _) => [...state, emptyBlockedWindow],
- },
- ],
thumbnailsChecked: [
props.contentSource.indexing.features.thumbnails.enabled,
{
@@ -176,6 +197,33 @@ export const SynchronizationLogic = kea<
return schedule;
},
+ addBlockedWindow: (state, _) => {
+ const schedule = cloneDeep(state);
+ const blockedWindows = schedule.blockedWindows || [];
+ blockedWindows.push(emptyBlockedWindow);
+ schedule.blockedWindows = blockedWindows;
+ return schedule;
+ },
+ removeBlockedWindow: (state, index) => {
+ const schedule = cloneDeep(state);
+ const blockedWindows = schedule.blockedWindows;
+ blockedWindows!.splice(index, 1);
+ if (blockedWindows!.length > 0) {
+ schedule.blockedWindows = blockedWindows;
+ } else {
+ delete schedule.blockedWindows;
+ }
+ return schedule;
+ },
+ setBlockedTimeWindow: (state, { index, prop, value }) => {
+ const schedule = cloneDeep(state);
+ const blockedWindows = schedule.blockedWindows;
+ const blockedWindow = blockedWindows![index] as BlockedWindowMap;
+ blockedWindow[prop] = value;
+ (blockedWindows![index] as BlockedWindowMap) = blockedWindow;
+ schedule.blockedWindows = blockedWindows;
+ return schedule;
+ },
},
],
}),
@@ -254,6 +302,7 @@ export const SynchronizationLogic = kea<
full: values.schedule.full,
incremental: values.schedule.incremental,
delete: values.schedule.delete,
+ blocked_windows: formatBlockedWindowsForServer(values.schedule.blockedWindows),
},
},
},
@@ -296,3 +345,16 @@ export const stripScheduleSeconds = (schedule: IndexingSchedule): IndexingSchedu
return _schedule;
};
+
+const formatBlockedWindowsForServer = (
+ blockedWindows?: BlockedWindow[]
+): ServerBlockedWindow[] | undefined => {
+ if (!blockedWindows || blockedWindows.length < 1) return [];
+
+ return blockedWindows.map(({ jobType, day, start, end }) => ({
+ job_type: jobType,
+ day,
+ start,
+ end,
+ }));
+};
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 91e32834f3fbd..14d0a7f196ae8 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
@@ -527,11 +527,11 @@ export const SOURCE_OVERVIEW_TITLE = i18n.translate(
}
);
-export const SOURCE_SYNCRONIZATION_DESCRIPTION = i18n.translate(
- 'xpack.enterpriseSearch.workplaceSearch.sources.sourceSyncronizationDescription',
+export const SOURCE_SYNCHRONIZATION_DESCRIPTION = i18n.translate(
+ 'xpack.enterpriseSearch.workplaceSearch.sources.sourceSynchronizationDescription',
{
defaultMessage:
- 'DO NOT TRANSLATE, temporary placeholder: Sync chupa chups dragée gummi bears jelly beans brownie. Fruitcake pie chocolate cake caramels carrot cake cotton candy dragée sweet roll soufflé.',
+ 'Synchronization provides control over data being indexed from the content source. Enable synchronization of data from the content source to Workplace Search.',
}
);
@@ -539,7 +539,7 @@ export const SOURCE_FREQUENCY_DESCRIPTION = i18n.translate(
'xpack.enterpriseSearch.workplaceSearch.sources.sourceFrequencyDescription',
{
defaultMessage:
- 'DO NOT TRANSLATE, temporary placeholder: Frequency chupa chups dragée gummi bears jelly beans brownie. Fruitcake pie chocolate cake caramels carrot cake cotton candy dragée sweet roll soufflé.',
+ 'Schedule the frequency of data synchronization between Workplace search and the content source. Indexing schedules that occur less frequently lower the burden on third-party servers, while more frequent will ensure your data is up-to-date.',
}
);
@@ -547,7 +547,7 @@ export const SOURCE_OBJECTS_AND_ASSETS_DESCRIPTION = i18n.translate(
'xpack.enterpriseSearch.workplaceSearch.sources.sourceObjectsAndAssetsDescription',
{
defaultMessage:
- 'DO NOT TRANSLATE, temporary placeholder: Objects chupa chups dragée gummi bears jelly beans brownie. Fruitcake pie chocolate cake caramels carrot cake cotton candy dragée sweet roll soufflé.',
+ 'Customize the indexing rules that determine what data is synchronized from this content source to Workplace Search.',
}
);
@@ -558,24 +558,24 @@ export const SOURCE_OBJECTS_AND_ASSETS_LABEL = i18n.translate(
}
);
-export const SOURCE_SYNCRONIZATION_TOGGLE_LABEL = i18n.translate(
- 'xpack.enterpriseSearch.workplaceSearch.sources.sourceSyncronizationToggleLabel',
+export const SOURCE_SYNCHRONIZATION_TOGGLE_LABEL = i18n.translate(
+ 'xpack.enterpriseSearch.workplaceSearch.sources.sourceSynchronizationToggleLabel',
{
defaultMessage: 'Synchronize this source',
}
);
-export const SOURCE_SYNCRONIZATION_TOGGLE_DESCRIPTION = i18n.translate(
- 'xpack.enterpriseSearch.workplaceSearch.sources.sourceSyncronizationToggleDescription',
+export const SOURCE_SYNCHRONIZATION_TOGGLE_DESCRIPTION = i18n.translate(
+ 'xpack.enterpriseSearch.workplaceSearch.sources.sourceSynchronizationToggleDescription',
{
defaultMessage: 'Source content will automatically be kept in sync.',
}
);
-export const SOURCE_SYNCRONIZATION_FREQUENCY_TITLE = i18n.translate(
- 'xpack.enterpriseSearch.workplaceSearch.sources.sourceSyncronizationFrequencyTitle',
+export const SOURCE_SYNCHRONIZATION_FREQUENCY_TITLE = i18n.translate(
+ 'xpack.enterpriseSearch.workplaceSearch.sources.sourceSynchronizationFrequencyTitle',
{
- defaultMessage: 'Syncronization frequency',
+ defaultMessage: 'Synchronization frequency',
}
);
@@ -614,24 +614,17 @@ export const SYNCHRONIZATION_DISABLED_DESCRIPTION = i18n.translate(
}
);
-export const DIFFERENT_SYNC_TYPES_LINK_LABEL = i18n.translate(
- 'xpack.enterpriseSearch.workplaceSearch.sources.differentSyncTypesLinkLabel',
+export const SYNC_FREQUENCY_LINK_LABEL = i18n.translate(
+ 'xpack.enterpriseSearch.workplaceSearch.sources.syncFrequencyLinkLabel',
{
- defaultMessage: 'Learn more about different sync types',
+ defaultMessage: 'Learn more about synchronization frequency',
}
);
-export const SYNC_BEST_PRACTICES_LINK_LABEL = i18n.translate(
- 'xpack.enterpriseSearch.workplaceSearch.sources.syncBestPracticesLinkLabel',
+export const OBJECTS_AND_ASSETS_LINK_LABEL = i18n.translate(
+ 'xpack.enterpriseSearch.workplaceSearch.sources.objectsAndAssetsLinkLabel',
{
- defaultMessage: 'Learn more about sync best practices',
- }
-);
-
-export const SYNC_OBJECTS_TYPES_LINK_LABEL = i18n.translate(
- 'xpack.enterpriseSearch.workplaceSearch.sources.syncObjectsTypesLinkLabel',
- {
- defaultMessage: 'Learn more about sync objects types',
+ defaultMessage: 'Learn more about Objects and assets',
}
);