Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add SLO cloning functionality #149935

Merged
merged 5 commits into from
Feb 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion x-pack/plugins/observability/public/data/slo/slo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ export const sloList: FindSLOResponse = {
},
{
...baseSlo,
id: 'c0f8d669-9177-4706-9098-f397a88173a6',
id: 'c0f8d669-9277-4706-9098-f397a88173a6',
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noticed React was complaining about duplicate ID's when running tests, this fixes that

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yes good catch I forgot to change the id for this one 🙈

summary: buildViolatedSummary(),
timeWindow: buildRollingTimeWindow({ duration: '7d' }),
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ export function useCreateOrUpdateSlo(): UseCreateOrUpdateSlo {
setSuccess(true);
} catch (e) {
setError(e);
} finally {
setSuccess(false);
setLoading(false);
Comment on lines +44 to +46
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think finally is called regardless of the success or failure of the try. So in this case, we always set success to false.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I was experimenting a bit with this pattern... will most likely revert

}
},
[http]
Expand All @@ -58,6 +61,9 @@ export function useCreateOrUpdateSlo(): UseCreateOrUpdateSlo {
setSuccess(true);
} catch (e) {
setError(e);
} finally {
setSuccess(false);
setLoading(false);
}
},
[http]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,11 @@ export function SloList() {
const [sort, setSort] = useState<SortType>('name');
const [indicatorTypeFilter, setIndicatorTypeFilter] = useState<FilterType[]>([]);

const [deleting, setIsDeleting] = useState(false);
const [isCloningOrDeleting, setIsCloningOrDeleting] = useState(false);
const [shouldReload, setShouldReload] = useState(false);

const {
loading,
loading: isLoadingSloList,
error,
sloList: { results: sloList = [], total, perPage },
} = useFetchSloList({
Expand All @@ -42,16 +42,19 @@ export function SloList() {
useEffect(() => {
if (shouldReload) {
setShouldReload(false);
setIsDeleting(false);
}
}, [shouldReload]);

const handleDeleted = () => {
setShouldReload(true);
if (!isLoadingSloList) {
setIsCloningOrDeleting(false);
}
}, [isLoadingSloList, shouldReload]);

const handleCloningOrDeleting = () => {
setIsCloningOrDeleting(true);
};

const handleDeleting = () => {
setIsDeleting(true);
const handleClonedOrDeleted = () => {
setShouldReload(true);
};

const handlePageClick = (pageNumber: number) => {
Expand Down Expand Up @@ -79,7 +82,7 @@ export function SloList() {
<EuiFlexGroup direction="column" gutterSize="m" data-test-subj="sloList">
<EuiFlexItem grow>
<SloListSearchFilterSortBar
loading={loading || deleting}
loading={isLoadingSloList || isCloningOrDeleting}
onChangeQuery={handleChangeQuery}
onChangeSort={handleChangeSort}
onChangeIndicatorTypeFilter={handleChangeIndicatorTypeFilter}
Expand All @@ -89,10 +92,12 @@ export function SloList() {
<EuiFlexItem>
<SloListItems
sloList={sloList}
loading={loading}
loading={isLoadingSloList}
error={error}
onDeleting={handleDeleting}
onDeleted={handleDeleted}
onCloned={handleClonedOrDeleted}
onCloning={handleCloningOrDeleting}
onDeleting={handleCloningOrDeleting}
onDeleted={handleClonedOrDeleted}
/>
</EuiFlexItem>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ const Template: ComponentStory<typeof Component> = (props: SloListItemProps) =>
const defaultProps = {
slo: buildSlo(),
historicalSummary: historicalSummaryData[HEALTHY_ROLLING_SLO],
onCloned: () => {},
onCloning: () => {},
onDeleted: () => {},
onDeleting: () => {},
};

export const SloListItem = Template.bind({});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* 2.0.
*/

import React, { useState } from 'react';
import React, { useEffect, useState } from 'react';
import {
EuiButtonIcon,
EuiContextMenuItem,
Expand All @@ -20,15 +20,22 @@ import { i18n } from '@kbn/i18n';

import { HistoricalSummaryResponse, SLOWithSummaryResponse } from '@kbn/slo-schema';
import { useKibana } from '../../../utils/kibana_react';
import { useCreateOrUpdateSlo } from '../../../hooks/slo/use_create_slo';
import { SloSummary } from './slo_summary';
import { SloDeleteConfirmationModal } from './slo_delete_confirmation_modal';
import { SloBadges } from './badges/slo_badges';
import {
transformSloResponseToCreateSloInput,
transformValuesToCreateSLOInput,
} from '../../slo_edit/helpers/process_slo_form_values';
import { paths } from '../../../config';

export interface SloListItemProps {
slo: SLOWithSummaryResponse;
historicalSummary?: HistoricalSummaryResponse[];
historicalSummaryLoading: boolean;
onCloned: () => void;
onCloning: () => void;
onDeleted: () => void;
onDeleting: () => void;
}
Expand All @@ -37,6 +44,8 @@ export function SloListItem({
slo,
historicalSummary = [],
historicalSummaryLoading,
onCloned,
onCloning,
onDeleted,
onDeleting,
}: SloListItemProps) {
Expand All @@ -45,6 +54,8 @@ export function SloListItem({
http: { basePath },
} = useKibana().services;

const { createSlo, loading: isCloning, success: isCloned } = useCreateOrUpdateSlo();

const [isActionsPopoverOpen, setIsActionsPopoverOpen] = useState(false);
const [isDeleteConfirmationModalOpen, setDeleteConfirmationModalOpen] = useState(false);
const [isDeleting, setIsDeleting] = useState(false);
Expand All @@ -57,6 +68,15 @@ export function SloListItem({
navigateToUrl(basePath.prepend(paths.observability.sloEdit(slo.id)));
};

const handleClone = () => {
const newSlo = transformValuesToCreateSLOInput(
transformSloResponseToCreateSloInput({ ...slo, name: `[Copy] ${slo.name}` })!
);

createSlo(newSlo);
setIsActionsPopoverOpen(false);
};

const handleDelete = () => {
setDeleteConfirmationModalOpen(true);
setIsDeleting(true);
Expand All @@ -73,12 +93,23 @@ export function SloListItem({
onDeleted();
};

useEffect(() => {
if (isCloning) {
onCloning();
}

if (isCloned) {
onCloned();
}
}, [isCloned, isCloning, onCloned, onCloning]);

return (
<EuiPanel
data-test-subj="sloItem"
hasBorder
hasShadow={false}
color={isDeleting ? 'subdued' : undefined}
style={{ opacity: isDeleting ? 0.3 : 1, transition: 'opacity 0.15s ease-in' }}
color={isCloning || isDeleting ? 'subdued' : undefined}
style={{ opacity: isCloning || isDeleting ? 0.3 : 1, transition: 'opacity 0.15s ease-in' }}
>
<EuiFlexGroup responsive={false} alignItems="center">
{/* CONTENT */}
Expand Down Expand Up @@ -124,12 +155,32 @@ export function SloListItem({
<EuiContextMenuPanel
size="s"
items={[
<EuiContextMenuItem key="edit" icon="pencil" onClick={handleEdit}>
<EuiContextMenuItem
key="edit"
icon="pencil"
onClick={handleEdit}
data-test-subj="sloActionsEdit"
>
{i18n.translate('xpack.observability.slos.slo.item.actions.edit', {
defaultMessage: 'Edit',
})}
</EuiContextMenuItem>,
<EuiContextMenuItem key="delete" icon="trash" onClick={handleDelete}>
<EuiContextMenuItem
key="clone"
icon="copy"
onClick={handleClone}
data-test-subj="sloActionsClone"
>
{i18n.translate('xpack.observability.slos.slo.item.actions.clone', {
defaultMessage: 'Clone',
})}
</EuiContextMenuItem>,
<EuiContextMenuItem
key="delete"
icon="trash"
onClick={handleDelete}
data-test-subj="sloActionsDelete"
>
{i18n.translate('xpack.observability.slos.slo.item.actions.delete', {
defaultMessage: 'Delete',
})}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ const defaultProps: Props = {
sloList: sloList.results,
loading: false,
error: false,
onCloned: () => {},
onCloning: () => {},
onDeleted: () => {},
onDeleting: () => {},
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,21 @@ export interface Props {
sloList: SLOWithSummaryResponse[];
loading: boolean;
error: boolean;
onCloned: () => void;
onCloning: () => void;
onDeleted: () => void;
onDeleting: () => void;
}

export function SloListItems({ sloList, loading, error, onDeleted, onDeleting }: Props) {
export function SloListItems({
sloList,
loading,
error,
onCloned,
onCloning,
onDeleted,
onDeleting,
}: Props) {
const [sloIds, setSloIds] = useState<string[]>([]);
useEffect(() => {
setSloIds(sloList.map((slo) => slo.id));
Expand All @@ -45,6 +55,8 @@ export function SloListItems({ sloList, loading, error, onDeleted, onDeleting }:
slo={slo}
historicalSummary={historicalSummaryBySlo[slo.id]}
historicalSummaryLoading={historicalSummaryLoading}
onCloned={onCloned}
onCloning={onCloning}
onDeleted={onDeleted}
onDeleting={onDeleting}
/>
Expand Down
Loading