[Workplace Search] Migrate Objects and assets from Source settings to Synchronization section (#113982) (#114015)
* Rename method We have to set the source from the sync logic file and this naming makes more sense * Wire up Enable Synchronization toggle * Remove sync controls from source settings * Refactor to pass in contentSource as prop Because we have a child logic file, SynchronizationLogic, we have to pass the content source into it for reading its values from SourceLogic. There are 3 ways to do this: 1. Access the source directly at SourceLogic.values.contentSource - This how we normally do it. The problem here is that SourceLogic is not mounted when the default values are set in the reducers. This caused the UI to break and I could not find a way to safely mount SourceLogic before this logic file needed it. 2. Use the connect property and connect to Sourcelogic to access contentSource - This actually worked great but our test helper does not work well with it and after an hour or so trying to make it work, I punted and decided to go with #3 below. 3. Pass the contentSource as a prop - This works great and is easy to test. The only drawback is that all other components that use the SynchronizationLogic file have to also pass in the content source. This commit does just that. * Add logic for Objects and assets view * Add content to Objects and assets view * Add fallback for `nextStart` that is in the past This is slightly beyond the scope of this PR but trying to make the final PR more manageable. There is an edge case where a running job lists the nextStart in the past if it is is running. After a lengthy Slack convo, it was decided to catch these in the UI and show a fallback string instead of something like “Next run 3 hours ago” * reduce -> map From previous PR feedback * Fix casing on i18n ID Co-authored-by: Scotty Bollinger <scotty.bollinger@elastic.co>
This commit is contained in:
parent
babfa59d1e
commit
3e05797525
|
@ -105,84 +105,6 @@ describe('SourceSettings', () => {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('handles disabling synchronization', () => {
|
|
||||||
const wrapper = shallow(<SourceSettings />);
|
|
||||||
|
|
||||||
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(<SourceSettings />);
|
|
||||||
|
|
||||||
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(<SourceSettings />);
|
|
||||||
|
|
||||||
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(<SourceSettings />);
|
|
||||||
|
|
||||||
const synchronizeSwitch = wrapper.find('[data-test-subj="ThumbnailsToggle"]');
|
|
||||||
|
|
||||||
expect(synchronizeSwitch.prop('disabled')).toEqual(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('DownloadDiagnosticsButton', () => {
|
describe('DownloadDiagnosticsButton', () => {
|
||||||
it('renders for org with correct href', () => {
|
it('renders for org with correct href', () => {
|
||||||
const wrapper = shallow(<SourceSettings />);
|
const wrapper = shallow(<SourceSettings />);
|
||||||
|
|
|
@ -17,8 +17,6 @@ import {
|
||||||
EuiFlexGroup,
|
EuiFlexGroup,
|
||||||
EuiFlexItem,
|
EuiFlexItem,
|
||||||
EuiFormRow,
|
EuiFormRow,
|
||||||
EuiSpacer,
|
|
||||||
EuiSwitch,
|
|
||||||
} from '@elastic/eui';
|
} from '@elastic/eui';
|
||||||
import { FormattedMessage } from '@kbn/i18n/react';
|
import { FormattedMessage } from '@kbn/i18n/react';
|
||||||
|
|
||||||
|
@ -50,12 +48,6 @@ import {
|
||||||
SYNC_DIAGNOSTICS_TITLE,
|
SYNC_DIAGNOSTICS_TITLE,
|
||||||
SYNC_DIAGNOSTICS_DESCRIPTION,
|
SYNC_DIAGNOSTICS_DESCRIPTION,
|
||||||
SYNC_DIAGNOSTICS_BUTTON,
|
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';
|
} from '../constants';
|
||||||
import { staticSourceData } from '../source_data';
|
import { staticSourceData } from '../source_data';
|
||||||
import { SourceLogic } from '../source_logic';
|
import { SourceLogic } from '../source_logic';
|
||||||
|
@ -70,22 +62,7 @@ export const SourceSettings: React.FC = () => {
|
||||||
const { getSourceConfigData } = useActions(AddSourceLogic);
|
const { getSourceConfigData } = useActions(AddSourceLogic);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
contentSource: {
|
contentSource: { name, id, serviceType, isOauth1 },
|
||||||
name,
|
|
||||||
id,
|
|
||||||
serviceType,
|
|
||||||
custom: isCustom,
|
|
||||||
isIndexedSource,
|
|
||||||
areThumbnailsConfigEnabled,
|
|
||||||
isOauth1,
|
|
||||||
indexing: {
|
|
||||||
enabled,
|
|
||||||
features: {
|
|
||||||
contentExtraction: { enabled: contentExtractionEnabled },
|
|
||||||
thumbnails: { enabled: thumbnailsEnabled },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
buttonLoading,
|
buttonLoading,
|
||||||
} = useValues(SourceLogic);
|
} = useValues(SourceLogic);
|
||||||
|
|
||||||
|
@ -109,11 +86,6 @@ export const SourceSettings: React.FC = () => {
|
||||||
const hideConfirm = () => setModalVisibility(false);
|
const hideConfirm = () => setModalVisibility(false);
|
||||||
|
|
||||||
const showConfig = isOrganization && !isEmpty(configuredFields);
|
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 || {};
|
const { clientId, clientSecret, publicKey, consumerKey, baseUrl } = configuredFields || {};
|
||||||
|
|
||||||
|
@ -130,18 +102,6 @@ export const SourceSettings: React.FC = () => {
|
||||||
updateContentSource(id, { name: inputValue });
|
updateContentSource(id, { name: inputValue });
|
||||||
};
|
};
|
||||||
|
|
||||||
const submitSyncControls = () => {
|
|
||||||
updateContentSource(id, {
|
|
||||||
indexing: {
|
|
||||||
enabled: synchronizeChecked,
|
|
||||||
features: {
|
|
||||||
content_extraction: { enabled: contentExtractionChecked },
|
|
||||||
thumbnails: { enabled: thumbnailsChecked },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSourceRemoval = () => {
|
const handleSourceRemoval = () => {
|
||||||
/**
|
/**
|
||||||
* The modal was just hanging while the UI waited for the server to respond.
|
* The modal was just hanging while the UI waited for the server to respond.
|
||||||
|
@ -221,58 +181,6 @@ export const SourceSettings: React.FC = () => {
|
||||||
</EuiFormRow>
|
</EuiFormRow>
|
||||||
</ContentSection>
|
</ContentSection>
|
||||||
)}
|
)}
|
||||||
{showSyncControls && (
|
|
||||||
<ContentSection title={SYNC_MANAGEMENT_TITLE} description={SYNC_MANAGEMENT_DESCRIPTION}>
|
|
||||||
<EuiFlexGroup>
|
|
||||||
<EuiFlexItem grow={false}>
|
|
||||||
<EuiSwitch
|
|
||||||
checked={synchronizeChecked}
|
|
||||||
onChange={(e) => setSynchronize(e.target.checked)}
|
|
||||||
label={SYNC_MANAGEMENT_SYNCHRONIZE_LABEL}
|
|
||||||
data-test-subj="SynchronizeToggle"
|
|
||||||
/>
|
|
||||||
</EuiFlexItem>
|
|
||||||
</EuiFlexGroup>
|
|
||||||
<EuiSpacer />
|
|
||||||
<EuiFlexGroup>
|
|
||||||
<EuiFlexItem grow={false}>
|
|
||||||
<EuiSwitch
|
|
||||||
checked={thumbnailsChecked}
|
|
||||||
onChange={(e) => setThumbnails(e.target.checked)}
|
|
||||||
label={
|
|
||||||
areThumbnailsConfigEnabled
|
|
||||||
? SYNC_MANAGEMENT_THUMBNAILS_LABEL
|
|
||||||
: SYNC_MANAGEMENT_THUMBNAILS_GLOBAL_CONFIG_LABEL
|
|
||||||
}
|
|
||||||
disabled={!areThumbnailsConfigEnabled}
|
|
||||||
data-test-subj="ThumbnailsToggle"
|
|
||||||
/>
|
|
||||||
</EuiFlexItem>
|
|
||||||
</EuiFlexGroup>
|
|
||||||
<EuiFlexGroup>
|
|
||||||
<EuiFlexItem grow={false}>
|
|
||||||
<EuiSwitch
|
|
||||||
checked={contentExtractionChecked}
|
|
||||||
onChange={(e) => setContentExtraction(e.target.checked)}
|
|
||||||
label={SYNC_MANAGEMENT_CONTENT_EXTRACTION_LABEL}
|
|
||||||
data-test-subj="ContentExtractionToggle"
|
|
||||||
/>
|
|
||||||
</EuiFlexItem>
|
|
||||||
</EuiFlexGroup>
|
|
||||||
<EuiSpacer />
|
|
||||||
<EuiFlexGroup>
|
|
||||||
<EuiFlexItem grow={false}>
|
|
||||||
<EuiButton
|
|
||||||
color="primary"
|
|
||||||
onClick={submitSyncControls}
|
|
||||||
data-test-subj="SaveSyncControlsButton"
|
|
||||||
>
|
|
||||||
{SAVE_CHANGES_BUTTON}
|
|
||||||
</EuiButton>
|
|
||||||
</EuiFlexItem>
|
|
||||||
</EuiFlexGroup>
|
|
||||||
</ContentSection>
|
|
||||||
)}
|
|
||||||
<ContentSection title={SYNC_DIAGNOSTICS_TITLE} description={SYNC_DIAGNOSTICS_DESCRIPTION}>
|
<ContentSection title={SYNC_DIAGNOSTICS_TITLE} description={SYNC_DIAGNOSTICS_DESCRIPTION}>
|
||||||
<EuiButton
|
<EuiButton
|
||||||
target="_blank"
|
target="_blank"
|
||||||
|
|
|
@ -10,7 +10,6 @@ import React from 'react';
|
||||||
import {
|
import {
|
||||||
EuiButton,
|
EuiButton,
|
||||||
EuiComboBox,
|
EuiComboBox,
|
||||||
EuiComboBoxOptionOption,
|
|
||||||
EuiDatePicker,
|
EuiDatePicker,
|
||||||
EuiFlexGroup,
|
EuiFlexGroup,
|
||||||
EuiFlexItem,
|
EuiFlexItem,
|
||||||
|
@ -81,13 +80,10 @@ const syncOptions = [
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const dayPickerOptions = DAYS_OF_WEEK_VALUES.reduce((options, day) => {
|
const dayPickerOptions = DAYS_OF_WEEK_VALUES.map((day) => ({
|
||||||
options.push({
|
|
||||||
label: DAYS_OF_WEEK_LABELS[day.toUpperCase() as keyof typeof DAYS_OF_WEEK_LABELS],
|
label: DAYS_OF_WEEK_LABELS[day.toUpperCase() as keyof typeof DAYS_OF_WEEK_LABELS],
|
||||||
value: day,
|
value: day,
|
||||||
});
|
}));
|
||||||
return options;
|
|
||||||
}, [] as Array<EuiComboBoxOptionOption<string>>);
|
|
||||||
|
|
||||||
export const BlockedWindowItem: React.FC<Props> = ({ blockedWindow }) => {
|
export const BlockedWindowItem: React.FC<Props> = ({ blockedWindow }) => {
|
||||||
const handleSyncTypeChange = () => '#TODO';
|
const handleSyncTypeChange = () => '#TODO';
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
import '../../../../../__mocks__/shallow_useeffect.mock';
|
import '../../../../../__mocks__/shallow_useeffect.mock';
|
||||||
import { setMockActions, setMockValues } from '../../../../../__mocks__/kea_logic';
|
import { setMockActions, setMockValues } from '../../../../../__mocks__/kea_logic';
|
||||||
|
import { fullContentSources } from '../../../../__mocks__/content_sources.mock';
|
||||||
import { blockedWindow } from './__mocks__/syncronization.mock';
|
import { blockedWindow } from './__mocks__/syncronization.mock';
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
@ -25,6 +26,7 @@ describe('BlockedWindows', () => {
|
||||||
};
|
};
|
||||||
const mockValues = {
|
const mockValues = {
|
||||||
blockedWindows: [blockedWindow],
|
blockedWindows: [blockedWindow],
|
||||||
|
contentSource: fullContentSources[0],
|
||||||
};
|
};
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
|
|
@ -13,13 +13,15 @@ import { EuiButton, EuiEmptyPrompt, EuiSpacer } from '@elastic/eui';
|
||||||
|
|
||||||
import { ADD_LABEL } from '../../../../constants';
|
import { ADD_LABEL } from '../../../../constants';
|
||||||
import { BLOCKED_EMPTY_STATE_TITLE, BLOCKED_EMPTY_STATE_DESCRIPTION } 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 { BlockedWindowItem } from './blocked_window_item';
|
||||||
import { SynchronizationLogic } from './synchronization_logic';
|
import { SynchronizationLogic } from './synchronization_logic';
|
||||||
|
|
||||||
export const BlockedWindows: React.FC = () => {
|
export const BlockedWindows: React.FC = () => {
|
||||||
const { blockedWindows } = useValues(SynchronizationLogic);
|
const { contentSource } = useValues(SourceLogic);
|
||||||
const { addBlockedWindow } = useActions(SynchronizationLogic);
|
const { blockedWindows } = useValues(SynchronizationLogic({ contentSource }));
|
||||||
|
const { addBlockedWindow } = useActions(SynchronizationLogic({ contentSource }));
|
||||||
|
|
||||||
const hasBlockedWindows = blockedWindows.length > 0;
|
const hasBlockedWindows = blockedWindows.length > 0;
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
import '../../../../../__mocks__/shallow_useeffect.mock';
|
import '../../../../../__mocks__/shallow_useeffect.mock';
|
||||||
import { setMockActions, setMockValues } from '../../../../../__mocks__/kea_logic';
|
import { setMockActions, setMockValues } from '../../../../../__mocks__/kea_logic';
|
||||||
|
import { fullContentSources } from '../../../../__mocks__/content_sources.mock';
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
|
@ -23,7 +24,9 @@ describe('Frequency', () => {
|
||||||
const mockActions = {
|
const mockActions = {
|
||||||
handleSelectedTabChanged,
|
handleSelectedTabChanged,
|
||||||
};
|
};
|
||||||
const mockValues = {};
|
const mockValues = {
|
||||||
|
contentSource: fullContentSources[0],
|
||||||
|
};
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
setMockActions(mockActions);
|
setMockActions(mockActions);
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { useActions } from 'kea';
|
import { useActions, useValues } from 'kea';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
EuiButton,
|
EuiButton,
|
||||||
|
@ -31,6 +31,7 @@ import {
|
||||||
DIFFERENT_SYNC_TYPES_LINK_LABEL,
|
DIFFERENT_SYNC_TYPES_LINK_LABEL,
|
||||||
SYNC_BEST_PRACTICES_LINK_LABEL,
|
SYNC_BEST_PRACTICES_LINK_LABEL,
|
||||||
} from '../../constants';
|
} from '../../constants';
|
||||||
|
import { SourceLogic } from '../../source_logic';
|
||||||
import { SourceLayout } from '../source_layout';
|
import { SourceLayout } from '../source_layout';
|
||||||
|
|
||||||
import { BlockedWindows } from './blocked_window_tab';
|
import { BlockedWindows } from './blocked_window_tab';
|
||||||
|
@ -42,7 +43,8 @@ interface FrequencyProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Frequency: React.FC<FrequencyProps> = ({ tabId }) => {
|
export const Frequency: React.FC<FrequencyProps> = ({ tabId }) => {
|
||||||
const { handleSelectedTabChanged } = useActions(SynchronizationLogic);
|
const { contentSource } = useValues(SourceLogic);
|
||||||
|
const { handleSelectedTabChanged } = useActions(SynchronizationLogic({ contentSource }));
|
||||||
|
|
||||||
const tabs = [
|
const tabs = [
|
||||||
{
|
{
|
||||||
|
|
|
@ -8,21 +8,24 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { shallow } from 'enzyme';
|
import { shallow } from 'enzyme';
|
||||||
|
import moment from 'moment';
|
||||||
|
|
||||||
import { EuiFieldNumber, EuiSuperSelect } from '@elastic/eui';
|
import { EuiFieldNumber, EuiSuperSelect } from '@elastic/eui';
|
||||||
|
|
||||||
import { FrequencyItem } from './frequency_item';
|
import { FrequencyItem } from './frequency_item';
|
||||||
|
|
||||||
describe('FrequencyItem', () => {
|
describe('FrequencyItem', () => {
|
||||||
|
const estimate = {
|
||||||
|
duration: 'PT3D',
|
||||||
|
nextStart: '2021-09-27T21:39:24+00:00',
|
||||||
|
lastRun: '2021-09-25T21:39:24+00:00',
|
||||||
|
};
|
||||||
|
|
||||||
const props = {
|
const props = {
|
||||||
label: 'Item',
|
label: 'Item',
|
||||||
description: 'My item',
|
description: 'My item',
|
||||||
duration: 'PT2D',
|
duration: 'PT2D',
|
||||||
estimate: {
|
estimate,
|
||||||
duration: 'PT3D',
|
|
||||||
nextStart: '2021-09-27T21:39:24+00:00',
|
|
||||||
lastRun: '2021-09-25T21:39:24+00:00',
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
it('renders', () => {
|
it('renders', () => {
|
||||||
|
@ -60,5 +63,25 @@ describe('FrequencyItem', () => {
|
||||||
expect(wrapper.find(EuiFieldNumber).prop('value')).toEqual(1);
|
expect(wrapper.find(EuiFieldNumber).prop('value')).toEqual(1);
|
||||||
expect(wrapper.find(EuiSuperSelect).prop('valueOfSelected')).toEqual('minutes');
|
expect(wrapper.find(EuiSuperSelect).prop('valueOfSelected')).toEqual('minutes');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('handles "nextStart" that is in past', () => {
|
||||||
|
const wrapper = shallow(<FrequencyItem {...props} />);
|
||||||
|
|
||||||
|
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(<FrequencyItem {...props} estimate={estimateWithPastNextStart} />);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
(wrapper.find('[data-test-subj="nextStartSummary"]').prop('values') as any)!.nextStartTime
|
||||||
|
).toEqual('in 2 days');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -27,6 +27,8 @@ import {
|
||||||
} from '../../../../../shared/constants';
|
} from '../../../../../shared/constants';
|
||||||
import { SyncEstimate } from '../../../../types';
|
import { SyncEstimate } from '../../../../types';
|
||||||
|
|
||||||
|
import { NEXT_SYNC_RUNNING_MESSAGE } from '../../constants';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
label: string;
|
label: string;
|
||||||
description: string;
|
description: string;
|
||||||
|
@ -53,6 +55,8 @@ export const FrequencyItem: React.FC<Props> = ({ label, description, duration, e
|
||||||
const [interval, unit] = formatDuration(duration);
|
const [interval, unit] = formatDuration(duration);
|
||||||
const { lastRun, nextStart, duration: durationEstimate } = estimate;
|
const { lastRun, nextStart, duration: durationEstimate } = estimate;
|
||||||
const estimateDisplay = durationEstimate && moment.duration(durationEstimate).humanize();
|
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';
|
const onChange = () => '#TODO';
|
||||||
|
|
||||||
|
@ -86,6 +90,7 @@ export const FrequencyItem: React.FC<Props> = ({ label, description, duration, e
|
||||||
|
|
||||||
const nextStartSummary = (
|
const nextStartSummary = (
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
|
data-test-subj="nextStartSummary"
|
||||||
id="xpack.enterpriseSearch.workplaceSearch.contentSources.synchronization.nextStartSummary"
|
id="xpack.enterpriseSearch.workplaceSearch.contentSources.synchronization.nextStartSummary"
|
||||||
defaultMessage="{nextStartStrong} will begin {nextStartTime}."
|
defaultMessage="{nextStartStrong} will begin {nextStartTime}."
|
||||||
values={{
|
values={{
|
||||||
|
@ -97,7 +102,7 @@ export const FrequencyItem: React.FC<Props> = ({ label, description, duration, e
|
||||||
/>
|
/>
|
||||||
</strong>
|
</strong>
|
||||||
),
|
),
|
||||||
nextStartTime: moment(nextStart).fromNow(),
|
nextStartTime,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
|
@ -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(<ObjectsAndAssets />);
|
||||||
|
|
||||||
|
expect(wrapper.find(EuiSwitch)).toHaveLength(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles thumbnails switch change', () => {
|
||||||
|
const wrapper = shallow(<ObjectsAndAssets />);
|
||||||
|
wrapper
|
||||||
|
.find('[data-test-subj="ThumbnailsToggle"]')
|
||||||
|
.simulate('change', { target: { checked: false } });
|
||||||
|
|
||||||
|
expect(setThumbnailsChecked).toHaveBeenCalledWith(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles content extraction switch change', () => {
|
||||||
|
const wrapper = shallow(<ObjectsAndAssets />);
|
||||||
|
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(<ObjectsAndAssets />);
|
||||||
|
|
||||||
|
expect(wrapper.find('[data-test-subj="ThumbnailsToggle"]').prop('label')).toEqual(
|
||||||
|
'Sync thumbnails - disabled at global configuration level'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
|
@ -7,33 +7,113 @@
|
||||||
|
|
||||||
import React from 'react';
|
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 { 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 { OBJECTS_AND_ASSETS_DOCS_URL } from '../../../../routes';
|
||||||
import {
|
import {
|
||||||
|
SYNC_MANAGEMENT_CONTENT_EXTRACTION_LABEL,
|
||||||
|
SYNC_MANAGEMENT_THUMBNAILS_LABEL,
|
||||||
|
SYNC_MANAGEMENT_THUMBNAILS_GLOBAL_CONFIG_LABEL,
|
||||||
SOURCE_OBJECTS_AND_ASSETS_DESCRIPTION,
|
SOURCE_OBJECTS_AND_ASSETS_DESCRIPTION,
|
||||||
SYNC_OBJECTS_TYPES_LINK_LABEL,
|
SYNC_OBJECTS_TYPES_LINK_LABEL,
|
||||||
|
SOURCE_OBJECTS_AND_ASSETS_LABEL,
|
||||||
|
SYNC_UNSAVED_CHANGES_MESSAGE,
|
||||||
} from '../../constants';
|
} from '../../constants';
|
||||||
|
import { SourceLogic } from '../../source_logic';
|
||||||
import { SourceLayout } from '../source_layout';
|
import { SourceLayout } from '../source_layout';
|
||||||
|
|
||||||
|
import { SynchronizationLogic } from './synchronization_logic';
|
||||||
|
|
||||||
export const ObjectsAndAssets: React.FC = () => {
|
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 = (
|
||||||
|
<EuiFlexGroup>
|
||||||
|
<EuiFlexItem>
|
||||||
|
<EuiButtonEmpty onClick={resetSyncSettings} disabled={!hasUnsavedObjectsAndAssetsChanges}>
|
||||||
|
{RESET_BUTTON}
|
||||||
|
</EuiButtonEmpty>
|
||||||
|
</EuiFlexItem>
|
||||||
|
<EuiFlexItem>
|
||||||
|
<EuiButton fill onClick={updateSyncSettings} disabled={!hasUnsavedObjectsAndAssetsChanges}>
|
||||||
|
{SAVE_BUTTON_LABEL}
|
||||||
|
</EuiButton>
|
||||||
|
</EuiFlexItem>
|
||||||
|
</EuiFlexGroup>
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SourceLayout
|
<SourceLayout
|
||||||
pageChrome={[NAV.SYNCHRONIZATION_OBJECTS_AND_ASSETS]}
|
pageChrome={[NAV.SYNCHRONIZATION_OBJECTS_AND_ASSETS]}
|
||||||
pageViewTelemetry="source_synchronization"
|
pageViewTelemetry="source_synchronization"
|
||||||
isLoading={false}
|
isLoading={dataLoading}
|
||||||
>
|
>
|
||||||
|
<UnsavedChangesPrompt
|
||||||
|
hasUnsavedChanges={hasUnsavedObjectsAndAssetsChanges}
|
||||||
|
messageText={SYNC_UNSAVED_CHANGES_MESSAGE}
|
||||||
|
/>
|
||||||
<ViewContentHeader
|
<ViewContentHeader
|
||||||
title={NAV.SYNCHRONIZATION_OBJECTS_AND_ASSETS}
|
title={NAV.SYNCHRONIZATION_OBJECTS_AND_ASSETS}
|
||||||
description={SOURCE_OBJECTS_AND_ASSETS_DESCRIPTION}
|
description={SOURCE_OBJECTS_AND_ASSETS_DESCRIPTION}
|
||||||
|
action={actions}
|
||||||
/>
|
/>
|
||||||
<EuiLink href={OBJECTS_AND_ASSETS_DOCS_URL} external>
|
<EuiLink href={OBJECTS_AND_ASSETS_DOCS_URL} external>
|
||||||
{SYNC_OBJECTS_TYPES_LINK_LABEL}
|
{SYNC_OBJECTS_TYPES_LINK_LABEL}
|
||||||
</EuiLink>
|
</EuiLink>
|
||||||
<EuiHorizontalRule />
|
<EuiHorizontalRule />
|
||||||
<div>TODO</div>
|
<EuiText size="m">{SOURCE_OBJECTS_AND_ASSETS_LABEL}</EuiText>
|
||||||
|
<EuiSpacer />
|
||||||
|
<EuiFlexGroup>
|
||||||
|
<EuiFlexItem grow={false}>
|
||||||
|
<EuiSwitch
|
||||||
|
checked={thumbnailsChecked}
|
||||||
|
onChange={(e) => setThumbnailsChecked(e.target.checked)}
|
||||||
|
label={
|
||||||
|
areThumbnailsConfigEnabled
|
||||||
|
? SYNC_MANAGEMENT_THUMBNAILS_LABEL
|
||||||
|
: SYNC_MANAGEMENT_THUMBNAILS_GLOBAL_CONFIG_LABEL
|
||||||
|
}
|
||||||
|
disabled={!areThumbnailsConfigEnabled}
|
||||||
|
data-test-subj="ThumbnailsToggle"
|
||||||
|
/>
|
||||||
|
</EuiFlexItem>
|
||||||
|
</EuiFlexGroup>
|
||||||
|
<EuiFlexGroup>
|
||||||
|
<EuiFlexItem grow={false}>
|
||||||
|
<EuiSwitch
|
||||||
|
checked={contentExtractionChecked}
|
||||||
|
onChange={(e) => setContentExtractionChecked(e.target.checked)}
|
||||||
|
label={SYNC_MANAGEMENT_CONTENT_EXTRACTION_LABEL}
|
||||||
|
data-test-subj="ContentExtractionToggle"
|
||||||
|
/>
|
||||||
|
</EuiFlexItem>
|
||||||
|
</EuiFlexGroup>
|
||||||
</SourceLayout>
|
</SourceLayout>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,7 +5,8 @@
|
||||||
* 2.0.
|
* 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';
|
import React from 'react';
|
||||||
|
|
||||||
|
@ -16,8 +17,15 @@ import { EuiLink, EuiCallOut, EuiSwitch } from '@elastic/eui';
|
||||||
import { Synchronization } from './synchronization';
|
import { Synchronization } from './synchronization';
|
||||||
|
|
||||||
describe('Synchronization', () => {
|
describe('Synchronization', () => {
|
||||||
|
const updateSyncEnabled = jest.fn();
|
||||||
|
const mockvalues = { contentSource: fullContentSources[0] };
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
setMockActions({ updateSyncEnabled });
|
||||||
|
setMockValues(mockvalues);
|
||||||
|
});
|
||||||
|
|
||||||
it('renders when config enabled', () => {
|
it('renders when config enabled', () => {
|
||||||
setMockValues({ contentSource: { isSyncConfigEnabled: true } });
|
|
||||||
const wrapper = shallow(<Synchronization />);
|
const wrapper = shallow(<Synchronization />);
|
||||||
|
|
||||||
expect(wrapper.find(EuiLink)).toHaveLength(1);
|
expect(wrapper.find(EuiLink)).toHaveLength(1);
|
||||||
|
@ -25,9 +33,16 @@ describe('Synchronization', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('renders when config disabled', () => {
|
it('renders when config disabled', () => {
|
||||||
setMockValues({ contentSource: { isSyncConfigEnabled: false } });
|
setMockValues({ contentSource: { isSyncConfigEnabled: false, indexing: { enabled: true } } });
|
||||||
const wrapper = shallow(<Synchronization />);
|
const wrapper = shallow(<Synchronization />);
|
||||||
|
|
||||||
expect(wrapper.find(EuiCallOut)).toHaveLength(1);
|
expect(wrapper.find(EuiCallOut)).toHaveLength(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('handles EuiSwitch change event', () => {
|
||||||
|
const wrapper = shallow(<Synchronization />);
|
||||||
|
wrapper.find(EuiSwitch).simulate('change', { target: { checked: true } });
|
||||||
|
|
||||||
|
expect(updateSyncEnabled).toHaveBeenCalled();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { useValues } from 'kea';
|
import { useActions, useValues } from 'kea';
|
||||||
|
|
||||||
import { EuiCallOut, EuiLink, EuiPanel, EuiSwitch, EuiSpacer, EuiText } from '@elastic/eui';
|
import { EuiCallOut, EuiLink, EuiPanel, EuiSwitch, EuiSpacer, EuiText } from '@elastic/eui';
|
||||||
|
|
||||||
|
@ -25,17 +25,23 @@ import {
|
||||||
import { SourceLogic } from '../../source_logic';
|
import { SourceLogic } from '../../source_logic';
|
||||||
import { SourceLayout } from '../source_layout';
|
import { SourceLayout } from '../source_layout';
|
||||||
|
|
||||||
export const Synchronization: React.FC = () => {
|
import { SynchronizationLogic } from './synchronization_logic';
|
||||||
const {
|
|
||||||
contentSource: { isSyncConfigEnabled },
|
|
||||||
} = useValues(SourceLogic);
|
|
||||||
|
|
||||||
const onChange = (checked: boolean) => `#TODO: ${checked}`;
|
export const Synchronization: React.FC = () => {
|
||||||
|
const { contentSource } = useValues(SourceLogic);
|
||||||
|
const { updateSyncEnabled } = useActions(SynchronizationLogic({ contentSource }));
|
||||||
|
|
||||||
|
const {
|
||||||
|
isSyncConfigEnabled,
|
||||||
|
indexing: { enabled },
|
||||||
|
} = contentSource;
|
||||||
|
|
||||||
|
const onChange = (checked: boolean) => updateSyncEnabled(checked);
|
||||||
const syncToggle = (
|
const syncToggle = (
|
||||||
<EuiPanel hasBorder>
|
<EuiPanel hasBorder>
|
||||||
<EuiSwitch
|
<EuiSwitch
|
||||||
label={SOURCE_SYNCRONIZATION_TOGGLE_LABEL}
|
label={SOURCE_SYNCRONIZATION_TOGGLE_LABEL}
|
||||||
checked
|
checked={enabled}
|
||||||
onChange={(e) => onChange(e.target.checked)}
|
onChange={(e) => onChange(e.target.checked)}
|
||||||
/>
|
/>
|
||||||
<EuiSpacer size="m" />
|
<EuiSpacer size="m" />
|
||||||
|
|
|
@ -5,14 +5,22 @@
|
||||||
* 2.0.
|
* 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';
|
import { nextTick } from '@kbn/test/jest';
|
||||||
|
|
||||||
const contentSource = { id: 'source123' };
|
import { expectedAsyncError } from '../../../../../test_helpers';
|
||||||
|
|
||||||
jest.mock('../../source_logic', () => ({
|
jest.mock('../../source_logic', () => ({
|
||||||
SourceLogic: { values: { contentSource } },
|
SourceLogic: { actions: { setContentSource: jest.fn() } },
|
||||||
}));
|
}));
|
||||||
|
import { SourceLogic } from '../../source_logic';
|
||||||
|
|
||||||
jest.mock('../../../../app_logic', () => ({
|
jest.mock('../../../../app_logic', () => ({
|
||||||
AppLogic: { values: { isOrganization: true } },
|
AppLogic: { values: { isOrganization: true } },
|
||||||
|
@ -21,17 +29,23 @@ jest.mock('../../../../app_logic', () => ({
|
||||||
import { SynchronizationLogic, emptyBlockedWindow } from './synchronization_logic';
|
import { SynchronizationLogic, emptyBlockedWindow } from './synchronization_logic';
|
||||||
|
|
||||||
describe('SynchronizationLogic', () => {
|
describe('SynchronizationLogic', () => {
|
||||||
|
const { http } = mockHttpValues;
|
||||||
|
const { flashAPIErrors, flashSuccessToast } = mockFlashMessageHelpers;
|
||||||
const { navigateToUrl } = mockKibanaValues;
|
const { navigateToUrl } = mockKibanaValues;
|
||||||
const { mount } = new LogicMounter(SynchronizationLogic);
|
const { mount } = new LogicMounter(SynchronizationLogic);
|
||||||
|
const contentSource = fullContentSources[0];
|
||||||
|
|
||||||
const defaultValues = {
|
const defaultValues = {
|
||||||
navigatingBetweenTabs: false,
|
navigatingBetweenTabs: false,
|
||||||
|
hasUnsavedObjectsAndAssetsChanges: false,
|
||||||
|
contentExtractionChecked: true,
|
||||||
|
thumbnailsChecked: true,
|
||||||
blockedWindows: [],
|
blockedWindows: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
jest.clearAllMocks();
|
jest.clearAllMocks();
|
||||||
mount();
|
mount({}, { contentSource });
|
||||||
});
|
});
|
||||||
|
|
||||||
it('has expected default values', () => {
|
it('has expected default values', () => {
|
||||||
|
@ -50,6 +64,18 @@ describe('SynchronizationLogic', () => {
|
||||||
|
|
||||||
expect(SynchronizationLogic.values.blockedWindows).toEqual([emptyBlockedWindow]);
|
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', () => {
|
describe('listeners', () => {
|
||||||
|
@ -63,7 +89,7 @@ describe('SynchronizationLogic', () => {
|
||||||
await nextTick();
|
await nextTick();
|
||||||
|
|
||||||
expect(setNavigatingBetweenTabsSpy).toHaveBeenCalledWith(true);
|
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 () => {
|
it('calls calls correct route for "blocked_time_windows"', async () => {
|
||||||
|
@ -71,9 +97,126 @@ describe('SynchronizationLogic', () => {
|
||||||
await nextTick();
|
await nextTick();
|
||||||
|
|
||||||
expect(navigateToUrl).toHaveBeenCalledWith(
|
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);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -10,6 +10,8 @@ import moment from 'moment';
|
||||||
|
|
||||||
export type TabId = 'source_sync_frequency' | 'blocked_time_windows';
|
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 { KibanaLogic } from '../../../../../shared/kibana';
|
||||||
import { AppLogic } from '../../../../app_logic';
|
import { AppLogic } from '../../../../app_logic';
|
||||||
import {
|
import {
|
||||||
|
@ -19,17 +21,30 @@ import {
|
||||||
} from '../../../../routes';
|
} from '../../../../routes';
|
||||||
import { BlockedWindow } from '../../../../types';
|
import { BlockedWindow } from '../../../../types';
|
||||||
|
|
||||||
|
import {
|
||||||
|
SYNC_ENABLED_MESSAGE,
|
||||||
|
SYNC_DISABLED_MESSAGE,
|
||||||
|
SYNC_SETTINGS_UPDATED_MESSAGE,
|
||||||
|
} from '../../constants';
|
||||||
import { SourceLogic } from '../../source_logic';
|
import { SourceLogic } from '../../source_logic';
|
||||||
|
|
||||||
interface SynchronizationActions {
|
interface SynchronizationActions {
|
||||||
setNavigatingBetweenTabs(navigatingBetweenTabs: boolean): boolean;
|
setNavigatingBetweenTabs(navigatingBetweenTabs: boolean): boolean;
|
||||||
handleSelectedTabChanged(tabId: TabId): TabId;
|
handleSelectedTabChanged(tabId: TabId): TabId;
|
||||||
addBlockedWindow(): void;
|
addBlockedWindow(): void;
|
||||||
|
updateSyncSettings(): void;
|
||||||
|
resetSyncSettings(): void;
|
||||||
|
updateSyncEnabled(enabled: boolean): boolean;
|
||||||
|
setThumbnailsChecked(checked: boolean): boolean;
|
||||||
|
setContentExtractionChecked(checked: boolean): boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SynchronizationValues {
|
interface SynchronizationValues {
|
||||||
hasUnsavedChanges: boolean;
|
|
||||||
navigatingBetweenTabs: boolean;
|
navigatingBetweenTabs: boolean;
|
||||||
|
hasUnsavedFrequencyChanges: boolean;
|
||||||
|
hasUnsavedObjectsAndAssetsChanges: boolean;
|
||||||
|
thumbnailsChecked: boolean;
|
||||||
|
contentExtractionChecked: boolean;
|
||||||
blockedWindows: BlockedWindow[];
|
blockedWindows: BlockedWindow[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,12 +58,18 @@ export const emptyBlockedWindow: BlockedWindow = {
|
||||||
export const SynchronizationLogic = kea<
|
export const SynchronizationLogic = kea<
|
||||||
MakeLogicType<SynchronizationValues, SynchronizationActions>
|
MakeLogicType<SynchronizationValues, SynchronizationActions>
|
||||||
>({
|
>({
|
||||||
|
path: ['enterprise_search', 'workplace_search', 'synchronization_logic'],
|
||||||
actions: {
|
actions: {
|
||||||
setNavigatingBetweenTabs: (navigatingBetweenTabs: boolean) => navigatingBetweenTabs,
|
setNavigatingBetweenTabs: (navigatingBetweenTabs: boolean) => navigatingBetweenTabs,
|
||||||
handleSelectedTabChanged: (tabId: TabId) => tabId,
|
handleSelectedTabChanged: (tabId: TabId) => tabId,
|
||||||
|
updateSyncEnabled: (enabled: boolean) => enabled,
|
||||||
|
setThumbnailsChecked: (checked: boolean) => checked,
|
||||||
|
setContentExtractionChecked: (checked: boolean) => checked,
|
||||||
|
updateSyncSettings: true,
|
||||||
|
resetSyncSettings: true,
|
||||||
addBlockedWindow: true,
|
addBlockedWindow: true,
|
||||||
},
|
},
|
||||||
reducers: {
|
reducers: ({ props }) => ({
|
||||||
navigatingBetweenTabs: [
|
navigatingBetweenTabs: [
|
||||||
false,
|
false,
|
||||||
{
|
{
|
||||||
|
@ -61,11 +82,47 @@ export const SynchronizationLogic = kea<
|
||||||
addBlockedWindow: (state, _) => [...state, emptyBlockedWindow],
|
addBlockedWindow: (state, _) => [...state, emptyBlockedWindow],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
thumbnailsChecked: [
|
||||||
|
props.contentSource.indexing.features.thumbnails.enabled,
|
||||||
|
{
|
||||||
|
setThumbnailsChecked: (_, thumbnailsChecked) => thumbnailsChecked,
|
||||||
},
|
},
|
||||||
listeners: ({ actions }) => ({
|
],
|
||||||
|
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) => {
|
handleSelectedTabChanged: async (tabId, breakpoint) => {
|
||||||
const { isOrganization } = AppLogic.values;
|
const { isOrganization } = AppLogic.values;
|
||||||
const { id: sourceId } = SourceLogic.values.contentSource;
|
const { id: sourceId } = props.contentSource;
|
||||||
const path =
|
const path =
|
||||||
tabId === 'source_sync_frequency'
|
tabId === 'source_sync_frequency'
|
||||||
? getContentSourcePath(SYNC_FREQUENCY_PATH, sourceId, isOrganization)
|
? getContentSourcePath(SYNC_FREQUENCY_PATH, sourceId, isOrganization)
|
||||||
|
@ -82,5 +139,51 @@ export const SynchronizationLogic = kea<
|
||||||
KibanaLogic.values.navigateToUrl(path);
|
KibanaLogic.values.navigateToUrl(path);
|
||||||
actions.setNavigatingBetweenTabs(false);
|
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);
|
||||||
|
}
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
|
@ -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(
|
export const SYNC_MANAGEMENT_SYNCHRONIZE_LABEL = i18n.translate(
|
||||||
'xpack.enterpriseSearch.workplaceSearch.contentSources.syncManagementSynchronizeLabel',
|
'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(
|
export const SYNC_MANAGEMENT_CONTENT_EXTRACTION_LABEL = i18n.translate(
|
||||||
'xpack.enterpriseSearch.workplaceSearch.contentSources.syncManagementContentExtractionLabel',
|
'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(
|
export const SOURCE_SYNCRONIZATION_TOGGLE_LABEL = i18n.translate(
|
||||||
'xpack.enterpriseSearch.workplaceSearch.sources.sourceSyncronizationToggleLabel',
|
'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.',
|
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',
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
|
@ -58,8 +58,8 @@ describe('SourceLogic', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('actions', () => {
|
describe('actions', () => {
|
||||||
it('onInitializeSource', () => {
|
it('setContentSource', () => {
|
||||||
SourceLogic.actions.onInitializeSource(contentSource);
|
SourceLogic.actions.setContentSource(contentSource);
|
||||||
|
|
||||||
expect(SourceLogic.values.contentSource).toEqual(contentSource);
|
expect(SourceLogic.values.contentSource).toEqual(contentSource);
|
||||||
expect(SourceLogic.values.dataLoading).toEqual(false);
|
expect(SourceLogic.values.dataLoading).toEqual(false);
|
||||||
|
@ -67,7 +67,7 @@ describe('SourceLogic', () => {
|
||||||
|
|
||||||
it('onUpdateSourceName', () => {
|
it('onUpdateSourceName', () => {
|
||||||
const NAME = 'foo';
|
const NAME = 'foo';
|
||||||
SourceLogic.actions.onInitializeSource(contentSource);
|
SourceLogic.actions.setContentSource(contentSource);
|
||||||
SourceLogic.actions.onUpdateSourceName(NAME);
|
SourceLogic.actions.onUpdateSourceName(NAME);
|
||||||
|
|
||||||
expect(SourceLogic.values.contentSource).toEqual({
|
expect(SourceLogic.values.contentSource).toEqual({
|
||||||
|
@ -88,7 +88,7 @@ describe('SourceLogic', () => {
|
||||||
it('setContentFilterValue', () => {
|
it('setContentFilterValue', () => {
|
||||||
const VALUE = 'bar';
|
const VALUE = 'bar';
|
||||||
SourceLogic.actions.setSearchResults(searchServerResponse);
|
SourceLogic.actions.setSearchResults(searchServerResponse);
|
||||||
SourceLogic.actions.onInitializeSource(contentSource);
|
SourceLogic.actions.setContentSource(contentSource);
|
||||||
SourceLogic.actions.setContentFilterValue(VALUE);
|
SourceLogic.actions.setContentFilterValue(VALUE);
|
||||||
|
|
||||||
expect(SourceLogic.values.contentMeta).toEqual({
|
expect(SourceLogic.values.contentMeta).toEqual({
|
||||||
|
@ -127,7 +127,7 @@ describe('SourceLogic', () => {
|
||||||
describe('listeners', () => {
|
describe('listeners', () => {
|
||||||
describe('initializeSource', () => {
|
describe('initializeSource', () => {
|
||||||
it('calls API and sets values (org)', async () => {
|
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);
|
const promise = Promise.resolve(contentSource);
|
||||||
http.get.mockReturnValue(promise);
|
http.get.mockReturnValue(promise);
|
||||||
SourceLogic.actions.initializeSource(contentSource.id);
|
SourceLogic.actions.initializeSource(contentSource.id);
|
||||||
|
@ -140,7 +140,7 @@ describe('SourceLogic', () => {
|
||||||
it('calls API and sets values (account)', async () => {
|
it('calls API and sets values (account)', async () => {
|
||||||
AppLogic.values.isOrganization = false;
|
AppLogic.values.isOrganization = false;
|
||||||
|
|
||||||
const onInitializeSourceSpy = jest.spyOn(SourceLogic.actions, 'onInitializeSource');
|
const onInitializeSourceSpy = jest.spyOn(SourceLogic.actions, 'setContentSource');
|
||||||
const promise = Promise.resolve(contentSource);
|
const promise = Promise.resolve(contentSource);
|
||||||
http.get.mockReturnValue(promise);
|
http.get.mockReturnValue(promise);
|
||||||
SourceLogic.actions.initializeSource(contentSource.id);
|
SourceLogic.actions.initializeSource(contentSource.id);
|
||||||
|
|
|
@ -23,7 +23,7 @@ import { PRIVATE_SOURCES_PATH, SOURCES_PATH, getSourcesPath } from '../../routes
|
||||||
import { ContentSourceFullData, Meta, DocumentSummaryItem, SourceContentItem } from '../../types';
|
import { ContentSourceFullData, Meta, DocumentSummaryItem, SourceContentItem } from '../../types';
|
||||||
|
|
||||||
export interface SourceActions {
|
export interface SourceActions {
|
||||||
onInitializeSource(contentSource: ContentSourceFullData): ContentSourceFullData;
|
setContentSource(contentSource: ContentSourceFullData): ContentSourceFullData;
|
||||||
onUpdateSourceName(name: string): string;
|
onUpdateSourceName(name: string): string;
|
||||||
setSearchResults(searchResultsResponse: SearchResultsResponse): SearchResultsResponse;
|
setSearchResults(searchResultsResponse: SearchResultsResponse): SearchResultsResponse;
|
||||||
initializeFederatedSummary(sourceId: string): { sourceId: string };
|
initializeFederatedSummary(sourceId: string): { sourceId: string };
|
||||||
|
@ -73,7 +73,7 @@ interface SourceUpdatePayload {
|
||||||
export const SourceLogic = kea<MakeLogicType<SourceValues, SourceActions>>({
|
export const SourceLogic = kea<MakeLogicType<SourceValues, SourceActions>>({
|
||||||
path: ['enterprise_search', 'workplace_search', 'source_logic'],
|
path: ['enterprise_search', 'workplace_search', 'source_logic'],
|
||||||
actions: {
|
actions: {
|
||||||
onInitializeSource: (contentSource: ContentSourceFullData) => contentSource,
|
setContentSource: (contentSource: ContentSourceFullData) => contentSource,
|
||||||
onUpdateSourceName: (name: string) => name,
|
onUpdateSourceName: (name: string) => name,
|
||||||
onUpdateSummary: (summary: object[]) => summary,
|
onUpdateSummary: (summary: object[]) => summary,
|
||||||
setSearchResults: (searchResultsResponse: SearchResultsResponse) => searchResultsResponse,
|
setSearchResults: (searchResultsResponse: SearchResultsResponse) => searchResultsResponse,
|
||||||
|
@ -93,7 +93,7 @@ export const SourceLogic = kea<MakeLogicType<SourceValues, SourceActions>>({
|
||||||
contentSource: [
|
contentSource: [
|
||||||
{} as ContentSourceFullData,
|
{} as ContentSourceFullData,
|
||||||
{
|
{
|
||||||
onInitializeSource: (_, contentSource) => contentSource,
|
setContentSource: (_, contentSource) => contentSource,
|
||||||
onUpdateSourceName: (contentSource, name) => ({
|
onUpdateSourceName: (contentSource, name) => ({
|
||||||
...contentSource,
|
...contentSource,
|
||||||
name,
|
name,
|
||||||
|
@ -108,7 +108,7 @@ export const SourceLogic = kea<MakeLogicType<SourceValues, SourceActions>>({
|
||||||
dataLoading: [
|
dataLoading: [
|
||||||
true,
|
true,
|
||||||
{
|
{
|
||||||
onInitializeSource: () => false,
|
setContentSource: () => false,
|
||||||
resetSourceState: () => true,
|
resetSourceState: () => true,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -158,7 +158,7 @@ export const SourceLogic = kea<MakeLogicType<SourceValues, SourceActions>>({
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await HttpLogic.values.http.get(route);
|
const response = await HttpLogic.values.http.get(route);
|
||||||
actions.onInitializeSource(response);
|
actions.setContentSource(response);
|
||||||
if (response.isFederatedSource) {
|
if (response.isFederatedSource) {
|
||||||
actions.initializeFederatedSummary(sourceId);
|
actions.initializeFederatedSummary(sourceId);
|
||||||
}
|
}
|
||||||
|
|
|
@ -10121,11 +10121,9 @@
|
||||||
"xpack.enterpriseSearch.workplaceSearch.contentSources.displaySettings.unsaved.message": "表示設定は保存されていません。終了してよろしいですか?",
|
"xpack.enterpriseSearch.workplaceSearch.contentSources.displaySettings.unsaved.message": "表示設定は保存されていません。終了してよろしいですか?",
|
||||||
"xpack.enterpriseSearch.workplaceSearch.contentSources.displaySettings.visibleFields.title": "表示フィールド",
|
"xpack.enterpriseSearch.workplaceSearch.contentSources.displaySettings.visibleFields.title": "表示フィールド",
|
||||||
"xpack.enterpriseSearch.workplaceSearch.contentSources.syncManagementContentExtractionLabel": "すべてのテキストとコンテンツを同期",
|
"xpack.enterpriseSearch.workplaceSearch.contentSources.syncManagementContentExtractionLabel": "すべてのテキストとコンテンツを同期",
|
||||||
"xpack.enterpriseSearch.workplaceSearch.contentSources.syncManagementDescription": "このソースの特定のコンテンツの抽出を有効および無効にします。",
|
|
||||||
"xpack.enterpriseSearch.workplaceSearch.contentSources.syncManagementGlobalConfigLabel": "サムネイルを同期 - グローバル構成レベルでは無効",
|
"xpack.enterpriseSearch.workplaceSearch.contentSources.syncManagementGlobalConfigLabel": "サムネイルを同期 - グローバル構成レベルでは無効",
|
||||||
"xpack.enterpriseSearch.workplaceSearch.contentSources.syncManagementSynchronizeLabel": "このソースを同期",
|
"xpack.enterpriseSearch.workplaceSearch.contentSources.syncManagementSynchronizeLabel": "このソースを同期",
|
||||||
"xpack.enterpriseSearch.workplaceSearch.contentSources.syncManagementThumbnailsLabel": "サムネイルを同期",
|
"xpack.enterpriseSearch.workplaceSearch.contentSources.syncManagementThumbnailsLabel": "サムネイルを同期",
|
||||||
"xpack.enterpriseSearch.workplaceSearch.contentSources.syncManagementTitle": "同期管理",
|
|
||||||
"xpack.enterpriseSearch.workplaceSearch.copyText": "コピー",
|
"xpack.enterpriseSearch.workplaceSearch.copyText": "コピー",
|
||||||
"xpack.enterpriseSearch.workplaceSearch.credentials.description": "クライアントで次の資格情報を使用して、認証サーバーからアクセストークンを要求します。",
|
"xpack.enterpriseSearch.workplaceSearch.credentials.description": "クライアントで次の資格情報を使用して、認証サーバーからアクセストークンを要求します。",
|
||||||
"xpack.enterpriseSearch.workplaceSearch.credentials.title": "資格情報",
|
"xpack.enterpriseSearch.workplaceSearch.credentials.title": "資格情報",
|
||||||
|
|
|
@ -10223,11 +10223,9 @@
|
||||||
"xpack.enterpriseSearch.workplaceSearch.contentSources.displaySettings.unsaved.message": "您的显示设置尚未保存。是否确定要离开?",
|
"xpack.enterpriseSearch.workplaceSearch.contentSources.displaySettings.unsaved.message": "您的显示设置尚未保存。是否确定要离开?",
|
||||||
"xpack.enterpriseSearch.workplaceSearch.contentSources.displaySettings.visibleFields.title": "可见的字段",
|
"xpack.enterpriseSearch.workplaceSearch.contentSources.displaySettings.visibleFields.title": "可见的字段",
|
||||||
"xpack.enterpriseSearch.workplaceSearch.contentSources.syncManagementContentExtractionLabel": "同步所有文本和内容",
|
"xpack.enterpriseSearch.workplaceSearch.contentSources.syncManagementContentExtractionLabel": "同步所有文本和内容",
|
||||||
"xpack.enterpriseSearch.workplaceSearch.contentSources.syncManagementDescription": "为此源启用和禁用特定内容的提取。",
|
|
||||||
"xpack.enterpriseSearch.workplaceSearch.contentSources.syncManagementGlobalConfigLabel": "同步缩略图 - 已在全局配置级别禁用",
|
"xpack.enterpriseSearch.workplaceSearch.contentSources.syncManagementGlobalConfigLabel": "同步缩略图 - 已在全局配置级别禁用",
|
||||||
"xpack.enterpriseSearch.workplaceSearch.contentSources.syncManagementSynchronizeLabel": "同步此源",
|
"xpack.enterpriseSearch.workplaceSearch.contentSources.syncManagementSynchronizeLabel": "同步此源",
|
||||||
"xpack.enterpriseSearch.workplaceSearch.contentSources.syncManagementThumbnailsLabel": "同步缩略图",
|
"xpack.enterpriseSearch.workplaceSearch.contentSources.syncManagementThumbnailsLabel": "同步缩略图",
|
||||||
"xpack.enterpriseSearch.workplaceSearch.contentSources.syncManagementTitle": "同步管理",
|
|
||||||
"xpack.enterpriseSearch.workplaceSearch.copyText": "复制",
|
"xpack.enterpriseSearch.workplaceSearch.copyText": "复制",
|
||||||
"xpack.enterpriseSearch.workplaceSearch.credentials.description": "在您的客户端中使用以下凭据从我们的身份验证服务器请求访问令牌。",
|
"xpack.enterpriseSearch.workplaceSearch.credentials.description": "在您的客户端中使用以下凭据从我们的身份验证服务器请求访问令牌。",
|
||||||
"xpack.enterpriseSearch.workplaceSearch.credentials.title": "凭据",
|
"xpack.enterpriseSearch.workplaceSearch.credentials.title": "凭据",
|
||||||
|
|
Loading…
Reference in a new issue