Skip to content

Commit

Permalink
[Workplace Search] Add read views for Source Sync Scheduling (#113199)
Browse files Browse the repository at this point in the history
* Add constants

Placeholders in place whle content is being written

* Update mock to match API and add types

The weird typing around `DaysOfWeek` was taken from this SO answer to get an array from a union type:

https://stackoverflow.com/a/45486495/1949235

* Add routes and stubbed docs urls

* Add components for list items

These components are the repeatable components in each of the frequency tabs.

- FrequencyItem
- BlockedWindowItem

Form changes methods are stubbed for now.

* Add tab components for Frequency page

* Add Frequency page component

* Add synchronization logic

This is just the basics. More will be added in a future PR

* Add Synchronization op-level page

* Add Synchronization router and subnav

* Add `Objects and assets` page stub

This is merely a placeholder page so the routes could be built out. Section will be moved from settings in a future PR

* Add name and new nav item to source sub nav

* Add SynchronizationRouter nav to Source router

* Fix a couple of typos

* Add callout and disable subnav for disabled sync

This was added to the API after the rest of the work was done, so adding it here.

https://github.com/elastic/workplace-search-team/issues/2043

* Update frequency item to account for edge case

There was a very long discussion about the edge case that is covered here.

https://github.com/elastic/ent-search/pull/4715

Basically here is what we landed on:

In most cases, the user will use the form to set the sync frequency, in which case the duration will be in the format of "PT3D" (ISO 8601). However, if an operator has set the sync frequency via the API, the duration could be a complex format, such as "P1DT2H3M4S". It was decided that in this case, we should omit seconds and go with the least common denominator from minutes.

Example: "P1DT2H3M4S" -> "1563 Minutes"

* Fix failing tests and add key

* Update constants with note for translators

* Fix typo

Co-authored-by: Vadim Yakhin <[email protected]>

Co-authored-by: Kibana Machine <[email protected]>
Co-authored-by: Vadim Yakhin <[email protected]>
  • Loading branch information
3 people authored Oct 5, 2021
1 parent 7446e07 commit 8f7dd3f
Show file tree
Hide file tree
Showing 32 changed files with 1,716 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@
export * from './actions';
export * from './labels';
export * from './tables';
export * from './units';
export { DEFAULT_META } from './default_meta';
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@

import { i18n } from '@kbn/i18n';

export const MINUTES_UNIT_LABEL = i18n.translate('xpack.enterpriseSearch.units.minutesLabel', {
defaultMessage: 'Minutes',
});

export const HOURS_UNIT_LABEL = i18n.translate('xpack.enterpriseSearch.units.hoursLabel', {
defaultMessage: 'Hours',
});
Expand All @@ -22,3 +26,27 @@ export const WEEKS_UNIT_LABEL = i18n.translate('xpack.enterpriseSearch.units.wee
export const MONTHS_UNIT_LABEL = i18n.translate('xpack.enterpriseSearch.units.monthsLabel', {
defaultMessage: 'Months',
});

export const DAYS_OF_WEEK_LABELS = {
SUNDAY: i18n.translate('xpack.enterpriseSearch.units.daysOfWeekLabel.sunday', {
defaultMessage: 'Sunday',
}),
MONDAY: i18n.translate('xpack.enterpriseSearch.units.daysOfWeekLabel.monday', {
defaultMessage: 'Monday',
}),
TUESDAY: i18n.translate('xpack.enterpriseSearch.units.daysOfWeekLabel.tuesday', {
defaultMessage: 'Tuesday',
}),
WEDNESDAY: i18n.translate('xpack.enterpriseSearch.units.daysOfWeekLabel.wednesday', {
defaultMessage: 'Wednesday',
}),
THURSDAY: i18n.translate('xpack.enterpriseSearch.units.daysOfWeekLabel.thursday', {
defaultMessage: 'Thursday',
}),
FRIDAY: i18n.translate('xpack.enterpriseSearch.units.daysOfWeekLabel.friday', {
defaultMessage: 'Friday',
}),
SATURDAY: i18n.translate('xpack.enterpriseSearch.units.daysOfWeekLabel.saturday', {
defaultMessage: 'Saturday',
}),
};
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,28 @@ const defaultIndexing = {
defaultAction: 'include',
rules: [],
schedule: {
intervals: [],
blocked: [],
full: 'P1D',
incremental: 'P2H',
delete: 'P10M',
permissions: 'P3H',
estimates: {
full: {
nextStart: '2021-09-30T15:37:38+00:00',
duration: 'PT1M5S',
},
incremental: {
nextStart: '2021-09-27T17:39:24+00:00',
duration: 'PT2S',
},
delete: {
nextStart: '2021-09-27T21:39:24+00:00',
duration: 'PT49S',
},
permissions: {
nextStart: '2021-09-27T17:39:24+00:00',
duration: 'PT2S',
},
},
},
features: {
contentExtraction: {
Expand Down Expand Up @@ -90,6 +110,7 @@ export const fullContentSources = [
groups,
custom: false,
isIndexedSource: true,
isSyncConfigEnabled: true,
areThumbnailsConfigEnabled: true,
accessToken: '123token',
urlField: 'myLink',
Expand All @@ -111,6 +132,7 @@ export const fullContentSources = [
indexing: defaultIndexing,
custom: true,
isIndexedSource: true,
isSyncConfigEnabled: true,
areThumbnailsConfigEnabled: true,
accessToken: '123token',
urlField: 'url',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,21 @@ export const NAV = {
SCHEMA: i18n.translate('xpack.enterpriseSearch.workplaceSearch.nav.schema', {
defaultMessage: 'Schema',
}),
SYNCHRONIZATION: i18n.translate('xpack.enterpriseSearch.workplaceSearch.nav.synchronization', {
defaultMessage: 'Synchronization',
}),
SYNCHRONIZATION_FREQUENCY: i18n.translate(
'xpack.enterpriseSearch.workplaceSearch.nav.synchronizationFrequency',
{
defaultMessage: 'Frequency',
}
),
SYNCHRONIZATION_OBJECTS_AND_ASSETS: i18n.translate(
'xpack.enterpriseSearch.workplaceSearch.nav.synchronizationObjectsAndAssets',
{
defaultMessage: 'Objects and assets',
}
),
DISPLAY_SETTINGS: i18n.translate('xpack.enterpriseSearch.workplaceSearch.nav.displaySettings', {
defaultMessage: 'Display Settings',
}),
Expand Down Expand Up @@ -745,6 +760,18 @@ export const DESCRIPTION_LABEL = i18n.translate(
}
);

export const BLOCK_LABEL = i18n.translate('xpack.enterpriseSearch.workplaceSearch.blockLabel', {
defaultMessage: 'Block',
});

export const BETWEEN_LABEL = i18n.translate('xpack.enterpriseSearch.workplaceSearch.betweenLabel', {
defaultMessage: 'between',
});

export const EVERY_LABEL = i18n.translate('xpack.enterpriseSearch.workplaceSearch.everyLabel', {
defaultMessage: 'every',
});

export const AND = i18n.translate('xpack.enterpriseSearch.workplaceSearch.and', {
defaultMessage: 'and',
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ 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 PERSONAL_PATH = '/p';

Expand Down Expand Up @@ -89,12 +93,17 @@ export const SOURCE_DETAILS_PATH = `${SOURCES_PATH}/:sourceId`;
export const SOURCE_CONTENT_PATH = `${SOURCES_PATH}/:sourceId/content`;
export const SOURCE_SCHEMAS_PATH = `${SOURCES_PATH}/:sourceId/schemas`;
export const SOURCE_DISPLAY_SETTINGS_PATH = `${SOURCES_PATH}/:sourceId/display_settings`;
export const SOURCE_SYNCHRONIZATION_PATH = `${SOURCES_PATH}/:sourceId/synchronization`;
export const SOURCE_SETTINGS_PATH = `${SOURCES_PATH}/:sourceId/settings`;
export const REINDEX_JOB_PATH = `${SOURCE_SCHEMAS_PATH}/:activeReindexJobId`;

export const DISPLAY_SETTINGS_SEARCH_RESULT_PATH = `${SOURCE_DISPLAY_SETTINGS_PATH}/`;
export const DISPLAY_SETTINGS_RESULT_DETAIL_PATH = `${SOURCE_DISPLAY_SETTINGS_PATH}/result_detail`;

export const SYNC_FREQUENCY_PATH = `${SOURCE_SYNCHRONIZATION_PATH}/frequency`;
export const BLOCKED_TIME_WINDOWS_PATH = `${SOURCE_SYNCHRONIZATION_PATH}/frequency/blocked_windows`;
export const OBJECTS_AND_ASSETS_PATH = `${SOURCE_SYNCHRONIZATION_PATH}/objects_and_assets`;

export const ORG_SETTINGS_PATH = '/settings';
export const ORG_SETTINGS_CUSTOMIZE_PATH = `${ORG_SETTINGS_PATH}/customize`;
export const ORG_SETTINGS_CONNECTORS_PATH = `${ORG_SETTINGS_PATH}/connectors`;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
* 2.0.
*/

import { Moment } from 'moment';

import { RoleMapping } from '../shared/types';

export * from '../../../common/types/workplace_search';
Expand Down Expand Up @@ -128,7 +130,44 @@ interface SourceActivity {
status: string;
}

interface IndexingConfig {
export interface SyncEstimate {
duration?: string;
nextStart: string;
lastRun?: string;
}

interface SyncIndexItem<T> {
full: T;
incremental: T;
delete: T;
permissions?: T;
}

interface IndexingSchedule extends SyncIndexItem<string> {
estimates: SyncIndexItem<SyncEstimate>;
}

export type SyncJobType = 'full' | 'incremental' | 'delete' | 'permissions';

export const DAYS_OF_WEEK_VALUES = [
'sunday',
'monday',
'tuesday',
'wednesday',
'thursday',
'friday',
'saturday',
] as const;
export type DayOfWeek = typeof DAYS_OF_WEEK_VALUES[number];

export interface BlockedWindow {
jobType: SyncJobType;
day: DayOfWeek;
start: Moment;
end: Moment;
}

export interface IndexingConfig {
enabled: boolean;
features: {
contentExtraction: {
Expand All @@ -138,6 +177,7 @@ interface IndexingConfig {
enabled: boolean;
};
};
schedule: IndexingSchedule;
}

export interface ContentSourceFullData extends ContentSourceDetails {
Expand All @@ -148,6 +188,7 @@ export interface ContentSourceFullData extends ContentSourceDetails {
indexing: IndexingConfig;
custom: boolean;
isIndexedSource: boolean;
isSyncConfigEnabled: boolean;
areThumbnailsConfigEnabled: boolean;
accessToken: string;
urlField: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

import { setMockValues } from '../../../../__mocks__/kea_logic';

import React from 'react';

jest.mock('../../../../shared/layout', () => ({
generateNavLink: jest.fn(({ to }) => ({ href: to })),
}));
Expand All @@ -21,9 +23,13 @@ describe('useSourceSubNav', () => {
});

it('returns EUI nav items', () => {
setMockValues({ isOrganization: true, contentSource: { id: '1' } });
setMockValues({ isOrganization: true, contentSource: { id: '1', name: 'foo' } });

expect(useSourceSubNav()).toEqual([
{
id: 'sourceName',
name: <strong>foo</strong>,
},
{
id: 'sourceOverview',
name: 'Overview',
Expand All @@ -43,9 +49,16 @@ describe('useSourceSubNav', () => {
});

it('returns extra nav items for custom sources', () => {
setMockValues({ isOrganization: true, contentSource: { id: '2', serviceType: 'custom' } });
setMockValues({
isOrganization: true,
contentSource: { id: '2', serviceType: 'custom', name: 'foo' },
});

expect(useSourceSubNav()).toEqual([
{
id: 'sourceName',
name: <strong>foo</strong>,
},
{
id: 'sourceOverview',
name: 'Overview',
Expand Down Expand Up @@ -74,10 +87,60 @@ describe('useSourceSubNav', () => {
]);
});

it('returns extra nav items for synchronization', () => {
setMockValues({
isOrganization: true,
contentSource: { id: '2', isIndexedSource: true, name: 'foo', isSyncConfigEnabled: true },
});

expect(useSourceSubNav()).toEqual([
{
id: 'sourceName',
name: <strong>foo</strong>,
},
{
id: 'sourceOverview',
name: 'Overview',
href: '/sources/2',
},
{
id: 'sourceContent',
name: 'Content',
href: '/sources/2/content',
},
{
id: 'sourceSynchronization',
name: 'Synchronization',
href: '/sources/2/synchronization',
items: [
{
id: 'sourceSynchronizationFrequency',
name: 'Frequency',
href: '/sources/2/synchronization/frequency',
},
{
id: 'sourceSynchronizationObjectsAndAssets',
name: 'Objects and assets',
href: '/sources/2/synchronization/objects_and_assets',
},
],
},
{
id: 'sourceSettings',
name: 'Settings',
href: '/sources/2/settings',
},
]);
});

it('returns nav links to personal dashboard when not on an organization page', () => {
setMockValues({ isOrganization: false, contentSource: { id: '3' } });
setMockValues({ isOrganization: false, contentSource: { id: '3', name: 'foo' } });

expect(useSourceSubNav()).toEqual([
{
id: 'sourceName',
name: <strong>foo</strong>,
},
{
id: 'sourceOverview',
name: 'Overview',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
* 2.0.
*/

import React from 'react';

import { useValues } from 'kea';

import { EuiSideNavItemType } from '@elastic/eui';
Expand All @@ -19,18 +21,29 @@ import {
SOURCE_SCHEMAS_PATH,
SOURCE_DISPLAY_SETTINGS_PATH,
SOURCE_SETTINGS_PATH,
SOURCE_SYNCHRONIZATION_PATH,
} from '../../../routes';
import { SourceLogic } from '../source_logic';

import { useSynchronizationSubNav } from './synchronization/synchronization_sub_nav';

export const useSourceSubNav = () => {
const { isOrganization } = useValues(AppLogic);
const syncSubnav = useSynchronizationSubNav();
const {
contentSource: { id, serviceType },
contentSource: { id, serviceType, isIndexedSource, name },
} = useValues(SourceLogic);

if (!id) return undefined;

const isCustom = serviceType === CUSTOM_SERVICE_TYPE;
const showSynchronization = isIndexedSource && isOrganization;

const navItems: Array<EuiSideNavItemType<unknown>> = [
{
id: 'sourceName',
name: <strong>{name}</strong>,
},
{
id: 'sourceOverview',
name: NAV.OVERVIEW,
Expand All @@ -43,7 +56,17 @@ export const useSourceSubNav = () => {
},
];

const isCustom = serviceType === CUSTOM_SERVICE_TYPE;
if (showSynchronization) {
navItems.push({
id: 'sourceSynchronization',
name: NAV.SYNCHRONIZATION,
...generateNavLink({
to: getContentSourcePath(SOURCE_SYNCHRONIZATION_PATH, id, isOrganization),
}),
items: syncSubnav,
});
}

if (isCustom) {
navItems.push({
id: 'sourceSchema',
Expand Down
Loading

0 comments on commit 8f7dd3f

Please sign in to comment.