import { kea , MakeLogicType } from 'kea' ; import { cloneDeep , isEqual } from 'lodash' ; import moment from 'moment' ; export type TabId = 'source_sync_frequency' | 'blocked_time_windows' ; import { flashAPIErrors , flashSuccessToast } from '../../../../../shared/flash_messages' ; import { HttpLogic } from '../../../../../shared/http' ; import { KibanaLogic } from '../../../../../shared/kibana' ; import { AppLogic } from '../../../../app_logic' ; import { SYNC_FREQUENCY_PATH , BLOCKED_TIME_WINDOWS_PATH , getContentSourcePath , } from '../../../../routes' ; import { BlockedWindow , DayOfWeek , IndexingSchedule , ContentSourceFullData , 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 ; start : string ; end : string ; } interface ServerSyncSettingsBody { content_source : { indexing : { enabled ? : boolean ; features ? : { content_extraction : { enabled : boolean ; } ; thumbnails : { enabled : boolean ; } ; } ; schedule ? : { full : string ; incremental : string ; delete : string ; permissions ? : string ; blocked_windows ? : ServerBlockedWindow [ ] ; } ; } ; } ; } interface SynchronizationActions { setNavigatingBetweenTabs ( navigatingBetweenTabs ) : boolean ; handleSelectedTabChanged ( tabId ) : TabId ; addBlockedWindow ( ) : void ; removeBlockedWindow ( index ) : number ; updateFrequencySettings ( ) : void ; updateObjectsAndAssetsSettings ( ) : void ; resetSyncSettings ( ) : void ; updateSyncEnabled ( enabled ) : boolean ; setThumbnailsChecked ( checked ) : boolean ; setSyncFrequency ( type , value , unit ) : { type : SyncJobType ; value : number ; unit : TimeUnit ; } ; setBlockedTimeWindow ( index , prop , value ) : { index : number ; prop : BlockedWindowPropType ; value : string ; } ; setContentExtractionChecked ( checked ) : boolean ; setServerSchedule ( schedule ) : IndexingSchedule ; updateServerSettings ( body ) : ServerSyncSettingsBody ; } interface SynchronizationValues { navigatingBetweenTabs : boolean ; hasUnsavedFrequencyChanges : boolean ; hasUnsavedObjectsAndAssetsChanges : boolean ; thumbnailsChecked : boolean ; contentExtractionChecked : boolean ; cachedSchedule : IndexingSchedule ; schedule : IndexingSchedule ; } export const emptyBlockedWindow = { jobType : 'full' , day : 'monday' , start : '11:00:00Z' , end : '13:00:00Z' , } ; type BlockedWindowMap = { [ prop in keyof BlockedWindow ] : SyncJobType | DayOfWeek | 'all' | string ; } ; export const SynchronizationLogic = kea < MakeLogicType < SynchronizationValues , SynchronizationActions > > ( { path : [ 'enterprise_search' , 'workplace_search' , 'synchronization_logic' ] , actions : { setNavigatingBetweenTabs : ( navigatingBetweenTabs ) => navigatingBetweenTabs , handleSelectedTabChanged : ( tabId ) => tabId , updateSyncEnabled : ( enabled ) => enabled , setThumbnailsChecked : ( checked ) => checked , setSyncFrequency : ( type , value , unit ) => ( { type , value , unit , } ) , setBlockedTimeWindow : ( index , prop , value ) => ( { index , prop , value , } ) , setContentExtractionChecked : ( checked ) => checked , updateServerSettings : ( body ) => body , setServerSchedule : ( schedule ) => schedule , removeBlockedWindow : ( index ) => index , updateFrequencySettings : true , updateObjectsAndAssetsSettings : true , resetSyncSettings : true , addBlockedWindow : true , } , reducers : ( { props } ) => ( { navigatingBetweenTabs : [ false , { setNavigatingBetweenTabs : ( _ , navigatingBetweenTabs ) => navigatingBetweenTabs , } , ] , thumbnailsChecked : [ props . contentSource . indexing . features . thumbnails . enabled , { setThumbnailsChecked : ( _ , thumbnailsChecked ) => thumbnailsChecked , resetSyncSettings : ( ) => props . contentSource . indexing . features . thumbnails . enabled , } , ] , contentExtractionChecked : [ props . contentSource . indexing . features . contentExtraction . enabled , { setContentExtractionChecked : ( _ , contentExtractionChecked ) => contentExtractionChecked , resetSyncSettings : ( ) => props . contentSource . indexing . features . contentExtraction . enabled , } , ] , cachedSchedule : [ stripScheduleSeconds ( props . contentSource . indexing . schedule ) , { setServerSchedule : ( _ , schedule ) => schedule , } , ] , schedule : [ stripScheduleSeconds ( props . contentSource . indexing . schedule ) , { resetSyncSettings : ( ) => stripScheduleSeconds ( props . contentSource . indexing . schedule ) , setServerSchedule : ( _ , schedule ) => schedule , setSyncFrequency : ( state , { type , value , unit } ) => { let currentValue ; const schedule = cloneDeep ( state ) ; const duration = schedule [ type ] ; switch ( unit ) { case 'days' : currentValue = moment . duration ( duration ) . days ( ) ; break ; case 'hours' : currentValue = moment . duration ( duration ) . hours ( ) ; break ; default : currentValue = moment . duration ( duration ) . minutes ( ) ; break ; } schedule [ type ] = moment . duration ( duration ) . subtract ( currentValue , unit ) . add ( value , unit ) . toISOString ( ) ; 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 ; } , } , ] , } ) , 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 ) ; } , ] , hasUnsavedFrequencyChanges : [ ( ) => [ selectors . cachedSchedule , selectors . schedule ] , ( cachedSchedule , schedule ) => ! isEqual ( cachedSchedule , schedule ) , ] , } ) , listeners : ( { actions , values , props } ) => ( { handleSelectedTabChanged : async ( tabId , breakpoint ) => { const { isOrganization } = AppLogic . values ; const { id : sourceId } = props . contentSource ; const path = tabId === 'source_sync_frequency' ? getContentSourcePath ( SYNC_FREQUENCY_PATH , sourceId , isOrganization ) : getContentSourcePath ( BLOCKED_TIME_WINDOWS_PATH , sourceId , isOrganization ) ; actions . setNavigatingBetweenTabs ( true ) ; await breakpoint ( ) ; KibanaLogic . values . navigateToUrl ( path ) ; actions . setNavigatingBetweenTabs ( false ) ; } , updateSyncEnabled : async ( enabled ) => { actions . updateServerSettings ( { content_source : { indexing : { enabled } , } , } ) ; } , updateObjectsAndAssetsSettings : ( ) => { actions . updateServerSettings ( { content_source : { indexing : { features : { content_extraction : { enabled : values . contentExtractionChecked } , thumbnails : { enabled : values . thumbnailsChecked } , } , } , } , } ) ; } , updateFrequencySettings : ( ) => { actions . updateServerSettings ( { content_source : { indexing : { schedule : { full : values . schedule . full , incremental : values . schedule . incremental , delete : values . schedule . delete , blocked_windows : formatBlockedWindowsForServer ( values . schedule . blockedWindows ) , } , } , } , } ) ; } , updateServerSettings : async ( body ) => { const { id : sourceId } = props . contentSource ; const route = `/internal/workplace_search/org/sources/${ sourceId }/settings` ; try { const response = await HttpLogic . values . http . patch < ContentSourceFullData > ( route , { body : JSON . stringify ( body ) , } ) ; SourceLogic . actions . setContentSource ( response ) ; SynchronizationLogic . actions . setServerSchedule ( response . indexing . schedule ) ; flashSuccessToast ( SYNC_SETTINGS_UPDATED_MESSAGE ) ; } catch ( e ) { flashAPIErrors ( e ) ; } } , } ) , } ) ; const SECONDS_IN_MS = 1000 ; const getDurationMS = ( time ) : number => moment . duration ( time ) . seconds ( ) * SECONDS_IN_MS ; const getISOStringWithoutSeconds = ( time ) : string => moment . duration ( time ) . subtract ( getDurationMS ( time ) ) . toISOString ( ) ; export const stripScheduleSeconds = ( schedule ) : IndexingSchedule => { const _schedule = cloneDeep ( schedule ) ; const { full , incremental , delete : _delete , permissions } = _schedule ; _schedule . full = getISOStringWithoutSeconds ( full ) ; _schedule . incremental = getISOStringWithoutSeconds ( incremental ) ; _schedule . delete = getISOStringWithoutSeconds ( _delete ) ; if ( permissions ) _schedule . permissions = getISOStringWithoutSeconds ( permissions ) ; return _schedule ; } ; const formatBlockedWindowsForServer = ( blockedWindows ? ) : ServerBlockedWindow [ ] | undefined => { if ( ! blockedWindows || blockedWindows . length < 1 ) return [ ] ; return blockedWindows . map ( ( { jobType , day , start , end } ) => ( { job_type : jobType , day , start , end , } ) ) ; } ;