Skip to content

Commit

Permalink
[8.x] [ML] Daylight saving time calendar events (elastic#193605) (ela…
Browse files Browse the repository at this point in the history
…stic#195262)

# Backport

This will backport the following commits from `main` to `8.x`:
- [[ML] Daylight saving time calendar events
(elastic#193605)](elastic#193605)

<!--- Backport version: 9.4.3 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sqren/backport)

<!--BACKPORT [{"author":{"name":"James
Gowdy","email":"jgowdy@elastic.co"},"sourceCommit":{"committedDate":"2024-10-07T13:35:25Z","message":"[ML]
Daylight saving time calendar events (elastic#193605)\n\nAdds new pages for
creating and managing DST calendars.\r\nCloses
https://github.com/elastic/kibana/issues/189469\r\n\r\nNew section added
to Settings home
page.\r\n\r\n![image](https://github.com/user-attachments/assets/9165906f-e571-46be-a5ac-bf7dc9cd2801)\r\n\r\nNew
page for listing DST calendars. The original calendar page does
not\r\nshow DST
calendars.\r\n\r\n![image](https://github.com/user-attachments/assets/32a64a31-b4e5-4516-85fd-19e63aa9d5c4)\r\n\r\nNew
page for creating DST calendars.\r\nThe ability to apply to all jobs and
add a description has been removed.\r\nIt is not possible manually add
events. Events are automatically\r\ngenerated for a selected time
zone.\r\n<img width=\"1170\"
alt=\"image\"\r\nsrc=\"https://github.com/user-attachments/assets/557b8d39-6c17-448a-aa30-a282d8a424a7\">\r\n\r\nIf
the selected time zone does not observe daylight savings, an
info\r\ncallout is displayed\r\n<img width=\"1178\"
alt=\"image\"\r\nsrc=\"https://github.com/user-attachments/assets/627043bf-0368-4ab3-8ca7-1931f9622387\">\r\n\r\n\r\nA
new DST calendar section is added to all AD job
wizards.\r\n\r\n![image](https://github.com/user-attachments/assets/6359192b-faac-4ffb-ad3e-b8193f40f02f)","sha":"2881b0423d099649243ee01887f27a1fb6dbffe7","branchLabelMapping":{"^v9.0.0$":"main","^v8.16.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":[":ml","Feature:Anomaly
Detection","v9.0.0","release_note:feature","v8.16.0","backport:version"],"title":"[ML]
Daylight saving time calendar
events","number":193605,"url":"https://github.com/elastic/kibana/pull/193605","mergeCommit":{"message":"[ML]
Daylight saving time calendar events (elastic#193605)\n\nAdds new pages for
creating and managing DST calendars.\r\nCloses
https://github.com/elastic/kibana/issues/189469\r\n\r\nNew section added
to Settings home
page.\r\n\r\n![image](https://github.com/user-attachments/assets/9165906f-e571-46be-a5ac-bf7dc9cd2801)\r\n\r\nNew
page for listing DST calendars. The original calendar page does
not\r\nshow DST
calendars.\r\n\r\n![image](https://github.com/user-attachments/assets/32a64a31-b4e5-4516-85fd-19e63aa9d5c4)\r\n\r\nNew
page for creating DST calendars.\r\nThe ability to apply to all jobs and
add a description has been removed.\r\nIt is not possible manually add
events. Events are automatically\r\ngenerated for a selected time
zone.\r\n<img width=\"1170\"
alt=\"image\"\r\nsrc=\"https://github.com/user-attachments/assets/557b8d39-6c17-448a-aa30-a282d8a424a7\">\r\n\r\nIf
the selected time zone does not observe daylight savings, an
info\r\ncallout is displayed\r\n<img width=\"1178\"
alt=\"image\"\r\nsrc=\"https://github.com/user-attachments/assets/627043bf-0368-4ab3-8ca7-1931f9622387\">\r\n\r\n\r\nA
new DST calendar section is added to all AD job
wizards.\r\n\r\n![image](https://github.com/user-attachments/assets/6359192b-faac-4ffb-ad3e-b8193f40f02f)","sha":"2881b0423d099649243ee01887f27a1fb6dbffe7"}},"sourceBranch":"main","suggestedTargetBranches":["8.x"],"targetPullRequestStates":[{"branch":"main","label":"v9.0.0","branchLabelMappingKey":"^v9.0.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/193605","number":193605,"mergeCommit":{"message":"[ML]
Daylight saving time calendar events (elastic#193605)\n\nAdds new pages for
creating and managing DST calendars.\r\nCloses
https://github.com/elastic/kibana/issues/189469\r\n\r\nNew section added
to Settings home
page.\r\n\r\n![image](https://github.com/user-attachments/assets/9165906f-e571-46be-a5ac-bf7dc9cd2801)\r\n\r\nNew
page for listing DST calendars. The original calendar page does
not\r\nshow DST
calendars.\r\n\r\n![image](https://github.com/user-attachments/assets/32a64a31-b4e5-4516-85fd-19e63aa9d5c4)\r\n\r\nNew
page for creating DST calendars.\r\nThe ability to apply to all jobs and
add a description has been removed.\r\nIt is not possible manually add
events. Events are automatically\r\ngenerated for a selected time
zone.\r\n<img width=\"1170\"
alt=\"image\"\r\nsrc=\"https://github.com/user-attachments/assets/557b8d39-6c17-448a-aa30-a282d8a424a7\">\r\n\r\nIf
the selected time zone does not observe daylight savings, an
info\r\ncallout is displayed\r\n<img width=\"1178\"
alt=\"image\"\r\nsrc=\"https://github.com/user-attachments/assets/627043bf-0368-4ab3-8ca7-1931f9622387\">\r\n\r\n\r\nA
new DST calendar section is added to all AD job
wizards.\r\n\r\n![image](https://github.com/user-attachments/assets/6359192b-faac-4ffb-ad3e-b8193f40f02f)","sha":"2881b0423d099649243ee01887f27a1fb6dbffe7"}},{"branch":"8.x","label":"v8.16.0","branchLabelMappingKey":"^v8.16.0$","isSourceBranch":false,"state":"NOT_CREATED"}]}]
BACKPORT-->

Co-authored-by: James Gowdy <jgowdy@elastic.co>
  • Loading branch information
kibanamachine and jgowdyelastic authored Oct 7, 2024
1 parent 1963138 commit 8811474
Show file tree
Hide file tree
Showing 41 changed files with 1,144 additions and 390 deletions.
3 changes: 3 additions & 0 deletions x-pack/plugins/ml/common/constants/locator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,11 @@ export const ML_PAGES = {
ANOMALY_DETECTION_MODULES_VIEW_OR_CREATE: 'modules/check_view_or_create',
SETTINGS: 'settings',
CALENDARS_MANAGE: 'settings/calendars_list',
CALENDARS_DST_MANAGE: 'settings/calendars_dst_list',
CALENDARS_NEW: 'settings/calendars_list/new_calendar',
CALENDARS_DST_NEW: 'settings/calendars_dst_list/new_calendar',
CALENDARS_EDIT: 'settings/calendars_list/edit_calendar',
CALENDARS_DST_EDIT: 'settings/calendars_dst_list/edit_calendar',
FILTER_LISTS_MANAGE: 'settings/filter_lists',
FILTER_LISTS_NEW: 'settings/filter_lists/new_filter_list',
FILTER_LISTS_EDIT: 'settings/filter_lists/edit_filter_list',
Expand Down
18 changes: 13 additions & 5 deletions x-pack/plugins/ml/common/types/calendars.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,24 @@
* 2.0.
*/

export type CalendarId = string;
import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey';

export interface Calendar {
calendar_id: CalendarId;
export type MlCalendarId = string;

export interface MlCalendar {
calendar_id: MlCalendarId;
description: string;
events: any[];
job_ids: string[];
total_job_count?: number;
}

export interface UpdateCalendar extends Calendar {
calendarId: CalendarId;
export interface UpdateCalendar extends MlCalendar {
calendarId: MlCalendarId;
}

export type MlCalendarEvent = estypes.MlCalendarEvent & {
force_time_shift?: number;
skip_result?: boolean;
skip_model_update?: boolean;
};
11 changes: 11 additions & 0 deletions x-pack/plugins/ml/common/types/locator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,9 @@ export type MlGenericUrlState = MLPageState<
| typeof ML_PAGES.DATA_FRAME_ANALYTICS_CREATE_JOB
| typeof ML_PAGES.OVERVIEW
| typeof ML_PAGES.CALENDARS_MANAGE
| typeof ML_PAGES.CALENDARS_DST_MANAGE
| typeof ML_PAGES.CALENDARS_NEW
| typeof ML_PAGES.CALENDARS_DST_NEW
| typeof ML_PAGES.FILTER_LISTS_MANAGE
| typeof ML_PAGES.FILTER_LISTS_NEW
| typeof ML_PAGES.SETTINGS
Expand Down Expand Up @@ -247,6 +249,14 @@ export type CalendarEditUrlState = MLPageState<
}
>;

export type CalendarDstEditUrlState = MLPageState<
typeof ML_PAGES.CALENDARS_DST_EDIT,
{
calendarId: string;
globalState?: MlCommonGlobalState;
}
>;

export type FilterEditUrlState = MLPageState<
typeof ML_PAGES.FILTER_LISTS_EDIT,
{
Expand Down Expand Up @@ -277,6 +287,7 @@ export type MlLocatorState =
| DataFrameAnalyticsUrlState
| DataFrameAnalyticsExplorationUrlState
| CalendarEditUrlState
| CalendarDstEditUrlState
| FilterEditUrlState
| MlGenericUrlState
| NotificationsUrlState
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ import type { CREATED_BY_LABEL } from '../../../../../../common/constants/new_jo
import { JOB_TYPE, SHARED_RESULTS_INDEX_NAME } from '../../../../../../common/constants/new_job';
import { collectAggs } from './util/general';
import { filterRuntimeMappings } from './util/filter_runtime_mappings';
import type { Calendar } from '../../../../../../common/types/calendars';
import type { MlCalendar } from '../../../../../../common/types/calendars';
import { mlCalendarService } from '../../../../services/calendar_service';
import { getDatafeedAggregations } from '../../../../../../common/util/datafeed_utils';
import { getFirstKeyInObject } from '../../../../../../common/util/object_utils';
Expand All @@ -58,7 +58,7 @@ export class JobCreator {
protected _indexPatternTitle: IndexPatternTitle = '';
protected _indexPatternDisplayName: string = '';
protected _job_config: Job;
protected _calendars: Calendar[];
protected _calendars: MlCalendar[];
protected _datafeed_config: Datafeed;
protected _detectors: Detector[];
protected _influencers: string[];
Expand Down Expand Up @@ -271,11 +271,11 @@ export class JobCreator {
this._job_config.groups = groups;
}

public get calendars(): Calendar[] {
public get calendars(): MlCalendar[] {
return this._calendars;
}

public set calendars(calendars: Calendar[]) {
public set calendars(calendars: MlCalendar[]) {
this._calendars = calendars;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,16 @@ export const AdditionalSection: FC<Props> = ({ additionalExpanded, setAdditional
<CustomUrlsSelection />
</EuiFlexItem>
</EuiFlexGroup>

<EuiSpacer />

<EuiFlexGroup gutterSize="xl" style={{ marginLeft: '0px', marginRight: '0px' }}>
<EuiFlexItem>
<CalendarsSelection />
</EuiFlexItem>
<EuiFlexItem />
<EuiFlexItem>
<CalendarsSelection isDst={true} />
</EuiFlexItem>
</EuiFlexGroup>
</section>
</EuiAccordion>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,24 @@ import {
EuiToolTip,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import {
filterCalendarsForDst,
separateCalendarsByType,
} from '../../../../../../../../../settings/calendars/dst_utils';
import { JobCreatorContext } from '../../../../../job_creator_context';
import { Description } from './description';
import { PLUGIN_ID } from '../../../../../../../../../../../common/constants/app';
import type { Calendar } from '../../../../../../../../../../../common/types/calendars';
import type { MlCalendar } from '../../../../../../../../../../../common/types/calendars';
import { useMlApi, useMlKibana } from '../../../../../../../../../contexts/kibana';
import { GLOBAL_CALENDAR } from '../../../../../../../../../../../common/constants/calendars';
import { ML_PAGES } from '../../../../../../../../../../../common/constants/locator';
import { DescriptionDst } from './description_dst';

interface Props {
isDst?: boolean;
}

export const CalendarsSelection: FC = () => {
export const CalendarsSelection: FC<Props> = ({ isDst = false }) => {
const {
services: {
application: { getUrlForApp },
Expand All @@ -37,19 +46,22 @@ export const CalendarsSelection: FC = () => {
const mlApi = useMlApi();

const { jobCreator, jobCreatorUpdate } = useContext(JobCreatorContext);
const [selectedCalendars, setSelectedCalendars] = useState<Calendar[]>(jobCreator.calendars);
const [selectedOptions, setSelectedOptions] = useState<Array<EuiComboBoxOptionOption<Calendar>>>(
[]
const [selectedCalendars, setSelectedCalendars] = useState<MlCalendar[]>(
filterCalendarsForDst(jobCreator.calendars, isDst)
);
const [options, setOptions] = useState<Array<EuiComboBoxOptionOption<Calendar>>>([]);
const [selectedOptions, setSelectedOptions] = useState<
Array<EuiComboBoxOptionOption<MlCalendar>>
>([]);
const [options, setOptions] = useState<Array<EuiComboBoxOptionOption<MlCalendar>>>([]);
const [isLoading, setIsLoading] = useState(false);

async function loadCalendars() {
setIsLoading(true);
const calendars = (await mlApi.calendars()).filter(
const { calendars, calendarsDst } = separateCalendarsByType(await mlApi.calendars());
const filteredCalendars = (isDst ? calendarsDst : calendars).filter(
(c) => c.job_ids.includes(GLOBAL_CALENDAR) === false
);
setOptions(calendars.map((c) => ({ label: c.calendar_id, value: c })));
setOptions(filteredCalendars.map((c) => ({ label: c.calendar_id, value: c })));
setSelectedOptions(selectedCalendars.map((c) => ({ label: c.calendar_id, value: c })));
setIsLoading(false);
}
Expand All @@ -60,12 +72,14 @@ export const CalendarsSelection: FC = () => {
}, []);

useEffect(() => {
jobCreator.calendars = selectedCalendars;
const { calendars, calendarsDst } = separateCalendarsByType(jobCreator.calendars);
const otherCalendars = isDst ? calendars : calendarsDst;
jobCreator.calendars = [...selectedCalendars, ...otherCalendars];
jobCreatorUpdate();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [selectedCalendars.join()]);

const comboBoxProps: EuiComboBoxProps<Calendar> = {
const comboBoxProps: EuiComboBoxProps<MlCalendar> = {
async: true,
options,
selectedOptions,
Expand All @@ -77,11 +91,13 @@ export const CalendarsSelection: FC = () => {
};

const manageCalendarsHref = getUrlForApp(PLUGIN_ID, {
path: ML_PAGES.CALENDARS_MANAGE,
path: isDst ? ML_PAGES.CALENDARS_DST_MANAGE : ML_PAGES.CALENDARS_MANAGE,
});

const Desc = isDst ? DescriptionDst : Description;

return (
<Description>
<Desc>
<EuiFlexGroup gutterSize="xs" alignItems="center">
<EuiFlexItem>
<EuiComboBox {...comboBoxProps} data-test-subj="mlJobWizardComboBoxCalendars" />
Expand Down Expand Up @@ -119,6 +135,6 @@ export const CalendarsSelection: FC = () => {
/>
</EuiLink>
</EuiText>
</Description>
</Desc>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* 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 type { FC, PropsWithChildren } from 'react';
import React, { memo } from 'react';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n-react';
import { EuiDescribedFormGroup, EuiFormRow, EuiLink } from '@elastic/eui';
import { useMlKibana } from '../../../../../../../../../contexts/kibana';

export const DescriptionDst: FC<PropsWithChildren<unknown>> = memo(({ children }) => {
const {
services: { docLinks },
} = useMlKibana();
const docsUrl = docLinks.links.ml.calendars;
const title = i18n.translate(
'xpack.ml.newJob.wizard.jobDetailsStep.additionalSection.calendarsDstSelection.title',
{
defaultMessage: 'DST Calendars',
}
);
return (
<EuiDescribedFormGroup
title={<h3>{title}</h3>}
description={
<FormattedMessage
id="xpack.ml.newJob.wizard.jobDetailsStep.additionalSection.calendarsDstSelection.description"
defaultMessage="A list of scheduled events you want to ignore, taking into account daylight saving time shifts. {learnMoreLink}"
values={{
learnMoreLink: (
<EuiLink href={docsUrl} target="_blank">
<FormattedMessage
id="xpack.ml.newJob.wizard.jobDetailsStep.additionalSection.calendarsDstSelection.learnMoreLinkText"
defaultMessage="Learn more"
/>
</EuiLink>
),
}}
/>
}
>
<EuiFormRow>
<>{children}</>
</EuiFormRow>
</EuiDescribedFormGroup>
);
});
9 changes: 9 additions & 0 deletions x-pack/plugins/ml/public/application/routing/breadcrumbs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,14 @@ export const CALENDAR_MANAGEMENT_BREADCRUMB: ChromeBreadcrumb = Object.freeze({
deepLinkId: 'ml:calendarSettings',
});

export const CALENDAR_DST_MANAGEMENT_BREADCRUMB: ChromeBreadcrumb = Object.freeze({
text: i18n.translate('xpack.ml.settings.breadcrumbs.calendarManagementLabel', {
defaultMessage: 'Calendar DST management',
}),
href: '/settings/calendars_dst_list',
deepLinkId: 'ml:calendarSettings',
});

export const FILTER_LISTS_BREADCRUMB: ChromeBreadcrumb = Object.freeze({
text: i18n.translate('xpack.ml.settings.breadcrumbs.filterListsLabel', {
defaultMessage: 'Filter lists',
Expand Down Expand Up @@ -160,6 +168,7 @@ const breadcrumbs = {
CHANGE_POINT_DETECTION,
CREATE_JOB_BREADCRUMB,
CALENDAR_MANAGEMENT_BREADCRUMB,
CALENDAR_DST_MANAGEMENT_BREADCRUMB,
FILTER_LISTS_BREADCRUMB,
SUPPLIED_CONFIGURATIONS,
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export const calendarListRouteFactory = (
title: i18n.translate('xpack.ml.settings.calendarList.docTitle', {
defaultMessage: 'Calendars',
}),
render: (props, deps) => <PageWrapper {...props} deps={deps} />,
render: (props, deps) => <PageWrapper {...props} deps={deps} isDst={false} />,
breadcrumbs: [
getBreadcrumbWithUrlForApp('ML_BREADCRUMB', navigateToPath, basePath),
getBreadcrumbWithUrlForApp('ANOMALY_DETECTION_BREADCRUMB', navigateToPath, basePath),
Expand All @@ -40,7 +40,24 @@ export const calendarListRouteFactory = (
],
});

const PageWrapper: FC<PageProps> = () => {
export const calendarDstListRouteFactory = (
navigateToPath: NavigateToPath,
basePath: string
): MlRoute => ({
path: createPath(ML_PAGES.CALENDARS_DST_MANAGE),
title: i18n.translate('xpack.ml.settings.calendarList.docTitle', {
defaultMessage: 'Calendars',
}),
render: (props, deps) => <PageWrapper {...props} deps={deps} isDst={true} />,
breadcrumbs: [
getBreadcrumbWithUrlForApp('ML_BREADCRUMB', navigateToPath, basePath),
getBreadcrumbWithUrlForApp('ANOMALY_DETECTION_BREADCRUMB', navigateToPath, basePath),
getBreadcrumbWithUrlForApp('SETTINGS_BREADCRUMB', navigateToPath, basePath),
getBreadcrumbWithUrlForApp('CALENDAR_DST_MANAGEMENT_BREADCRUMB'),
],
});

const PageWrapper: FC<PageProps & { isDst: boolean }> = ({ isDst }) => {
const { context } = useRouteResolver('full', ['canGetCalendars'], { getMlNodeCount });

useTimefilter({ timeRangeSelector: false, autoRefreshSelector: false });
Expand All @@ -52,7 +69,7 @@ const PageWrapper: FC<PageProps> = () => {

return (
<PageLoader context={context}>
<CalendarsList {...{ canCreateCalendar, canDeleteCalendar }} />
<CalendarsList {...{ canCreateCalendar, canDeleteCalendar, isDst }} />
</PageLoader>
);
};
Loading

0 comments on commit 8811474

Please sign in to comment.