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

[7.x] [Logs UI] Fix initial selection of log threshold alert condition field if missing from mapping (#86488) #86819

Merged
merged 2 commits into from
Dec 23, 2020
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
74 changes: 57 additions & 17 deletions x-pack/plugins/infra/common/alerting/logs/log_threshold/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,29 +105,38 @@ const ThresholdRT = rt.type({

export type Threshold = rt.TypeOf<typeof ThresholdRT>;

export const CriterionRT = rt.type({
export const criterionRT = rt.type({
field: rt.string,
comparator: ComparatorRT,
value: rt.union([rt.string, rt.number]),
});
export type Criterion = rt.TypeOf<typeof criterionRT>;

export type Criterion = rt.TypeOf<typeof CriterionRT>;
export const criteriaRT = rt.array(CriterionRT);
export type Criteria = rt.TypeOf<typeof criteriaRT>;
export const partialCriterionRT = rt.partial(criterionRT.props);
export type PartialCriterion = rt.TypeOf<typeof partialCriterionRT>;

export const countCriteriaRT = criteriaRT;
export const countCriteriaRT = rt.array(criterionRT);
export type CountCriteria = rt.TypeOf<typeof countCriteriaRT>;

export const ratioCriteriaRT = rt.tuple([criteriaRT, criteriaRT]);
export const partialCountCriteriaRT = rt.array(partialCriterionRT);
export type PartialCountCriteria = rt.TypeOf<typeof partialCountCriteriaRT>;

export const ratioCriteriaRT = rt.tuple([countCriteriaRT, countCriteriaRT]);
export type RatioCriteria = rt.TypeOf<typeof ratioCriteriaRT>;

export const TimeUnitRT = rt.union([
export const partialRatioCriteriaRT = rt.tuple([partialCountCriteriaRT, partialCountCriteriaRT]);
export type PartialRatioCriteria = rt.TypeOf<typeof partialRatioCriteriaRT>;

export const partialCriteriaRT = rt.union([partialCountCriteriaRT, partialRatioCriteriaRT]);
export type PartialCriteria = rt.TypeOf<typeof partialCriteriaRT>;

export const timeUnitRT = rt.union([
rt.literal('s'),
rt.literal('m'),
rt.literal('h'),
rt.literal('d'),
]);
export type TimeUnit = rt.TypeOf<typeof TimeUnitRT>;
export type TimeUnit = rt.TypeOf<typeof timeUnitRT>;

export const timeSizeRT = rt.number;
export const groupByRT = rt.array(rt.string);
Expand All @@ -136,15 +145,18 @@ const RequiredAlertParamsRT = rt.type({
// NOTE: "count" would be better named as "threshold", but this would require a
// migration of encrypted saved objects, so we'll keep "count" until it's problematic.
count: ThresholdRT,
timeUnit: TimeUnitRT,
timeUnit: timeUnitRT,
timeSize: timeSizeRT,
});

const partialRequiredAlertParamsRT = rt.partial(RequiredAlertParamsRT.props);
export type PartialRequiredAlertParams = rt.TypeOf<typeof partialRequiredAlertParamsRT>;

const OptionalAlertParamsRT = rt.partial({
groupBy: groupByRT,
});

export const alertParamsRT = rt.intersection([
export const countAlertParamsRT = rt.intersection([
rt.type({
criteria: countCriteriaRT,
...RequiredAlertParamsRT.props,
Expand All @@ -153,8 +165,18 @@ export const alertParamsRT = rt.intersection([
...OptionalAlertParamsRT.props,
}),
]);
export type CountAlertParams = rt.TypeOf<typeof countAlertParamsRT>;

export type CountAlertParams = rt.TypeOf<typeof alertParamsRT>;
export const partialCountAlertParamsRT = rt.intersection([
rt.type({
criteria: partialCountCriteriaRT,
...RequiredAlertParamsRT.props,
}),
rt.partial({
...OptionalAlertParamsRT.props,
}),
]);
export type PartialCountAlertParams = rt.TypeOf<typeof partialCountAlertParamsRT>;

export const ratioAlertParamsRT = rt.intersection([
rt.type({
Expand All @@ -165,25 +187,43 @@ export const ratioAlertParamsRT = rt.intersection([
...OptionalAlertParamsRT.props,
}),
]);

export type RatioAlertParams = rt.TypeOf<typeof ratioAlertParamsRT>;

export const AlertParamsRT = rt.union([alertParamsRT, ratioAlertParamsRT]);
export type AlertParams = rt.TypeOf<typeof AlertParamsRT>;
export const partialRatioAlertParamsRT = rt.intersection([
rt.type({
criteria: partialRatioCriteriaRT,
...RequiredAlertParamsRT.props,
}),
rt.partial({
...OptionalAlertParamsRT.props,
}),
]);
export type PartialRatioAlertParams = rt.TypeOf<typeof partialRatioAlertParamsRT>;

export const alertParamsRT = rt.union([countAlertParamsRT, ratioAlertParamsRT]);
export type AlertParams = rt.TypeOf<typeof alertParamsRT>;

export const partialAlertParamsRT = rt.union([
partialCountAlertParamsRT,
partialRatioAlertParamsRT,
]);
export type PartialAlertParams = rt.TypeOf<typeof partialAlertParamsRT>;

export const isRatioAlert = (criteria: AlertParams['criteria']): criteria is RatioCriteria => {
export const isRatioAlert = (criteria: PartialCriteria): criteria is PartialRatioCriteria => {
return criteria.length > 0 && Array.isArray(criteria[0]) ? true : false;
};

export const isRatioAlertParams = (params: AlertParams): params is RatioAlertParams => {
return isRatioAlert(params.criteria);
};

export const getNumerator = (criteria: RatioCriteria): Criteria => {
export const getNumerator = <C extends RatioCriteria | PartialRatioCriteria>(criteria: C): C[0] => {
return criteria[0];
};

export const getDenominator = (criteria: RatioCriteria): Criteria => {
export const getDenominator = <C extends RatioCriteria | PartialRatioCriteria>(
criteria: C
): C[1] => {
return criteria[1];
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@

import * as rt from 'io-ts';
import {
criteriaRT,
TimeUnitRT,
countCriteriaRT,
timeUnitRT,
timeSizeRT,
groupByRT,
} from '../../alerting/logs/log_threshold/types';
Expand Down Expand Up @@ -42,8 +42,8 @@ export type GetLogAlertsChartPreviewDataSuccessResponsePayload = rt.TypeOf<

export const getLogAlertsChartPreviewDataAlertParamsSubsetRT = rt.intersection([
rt.type({
criteria: criteriaRT,
timeUnit: TimeUnitRT,
criteria: countCriteriaRT,
timeUnit: timeUnitRT,
timeSize: timeSizeRT,
}),
rt.partial({
Expand Down
3 changes: 3 additions & 0 deletions x-pack/plugins/infra/common/utility_types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,6 @@ export type DeepPartial<T> = T extends any[]
interface DeepPartialArray<T> extends Array<DeepPartial<T>> {}

type DeepPartialObject<T> = { [P in keyof T]+?: DeepPartial<T[P]> };

export type ObjectEntry<T> = [keyof T, T[keyof T]];
export type ObjectEntries<T> = Array<ObjectEntry<T>>;
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,11 @@ import { i18n } from '@kbn/i18n';
import { IFieldType } from 'src/plugins/data/public';
import { Criterion } from './criterion';
import {
AlertParams,
Comparator,
Criteria as CriteriaType,
Criterion as CriterionType,
CountCriteria as CountCriteriaType,
RatioCriteria as RatioCriteriaType,
PartialAlertParams,
PartialCountCriteria as PartialCountCriteriaType,
PartialCriteria as PartialCriteriaType,
PartialCriterion as PartialCriterionType,
PartialRatioCriteria as PartialRatioCriteriaType,
isRatioAlert,
getNumerator,
getDenominator,
Expand All @@ -25,8 +24,6 @@ import { Errors, CriterionErrors } from '../../validation';
import { ExpressionLike } from './editor';
import { CriterionPreview } from './criterion_preview_chart';

const DEFAULT_CRITERIA = { field: 'log.level', comparator: Comparator.EQ, value: 'error' };

const QueryAText = i18n.translate('xpack.infra.logs.alerting.threshold.ratioCriteriaQueryAText', {
defaultMessage: 'Query A',
});
Expand All @@ -37,11 +34,12 @@ const QueryBText = i18n.translate('xpack.infra.logs.alerting.threshold.ratioCrit

interface SharedProps {
fields: IFieldType[];
criteria?: AlertParams['criteria'];
criteria?: PartialCriteriaType;
defaultCriterion: PartialCriterionType;
errors: Errors['criteria'];
alertParams: Partial<AlertParams>;
alertParams: PartialAlertParams;
sourceId: string;
updateCriteria: (criteria: AlertParams['criteria']) => void;
updateCriteria: (criteria: PartialCriteriaType) => void;
}

type CriteriaProps = SharedProps;
Expand All @@ -60,10 +58,10 @@ export const Criteria: React.FC<CriteriaProps> = (props) => {
interface CriteriaWrapperProps {
alertParams: SharedProps['alertParams'];
fields: SharedProps['fields'];
updateCriterion: (idx: number, params: Partial<CriterionType>) => void;
updateCriterion: (idx: number, params: PartialCriterionType) => void;
removeCriterion: (idx: number) => void;
addCriterion: () => void;
criteria: CriteriaType;
criteria: PartialCountCriteriaType;
errors: CriterionErrors;
sourceId: SharedProps['sourceId'];
isRatio?: boolean;
Expand Down Expand Up @@ -118,29 +116,24 @@ const CriteriaWrapper: React.FC<CriteriaWrapperProps> = (props) => {
);
};

interface RatioCriteriaProps {
alertParams: SharedProps['alertParams'];
fields: SharedProps['fields'];
criteria: RatioCriteriaType;
errors: Errors['criteria'];
sourceId: SharedProps['sourceId'];
updateCriteria: (criteria: AlertParams['criteria']) => void;
interface RatioCriteriaProps extends SharedProps {
criteria: PartialRatioCriteriaType;
}

const RatioCriteria: React.FC<RatioCriteriaProps> = (props) => {
const { criteria, errors, updateCriteria } = props;
const { criteria, defaultCriterion, errors, updateCriteria } = props;

const handleUpdateNumeratorCriteria = useCallback(
(criteriaParam: CriteriaType) => {
const nextCriteria: RatioCriteriaType = [criteriaParam, getDenominator(criteria)];
(criteriaParam: PartialCountCriteriaType) => {
const nextCriteria: PartialRatioCriteriaType = [criteriaParam, getDenominator(criteria)];
updateCriteria(nextCriteria);
},
[updateCriteria, criteria]
);

const handleUpdateDenominatorCriteria = useCallback(
(criteriaParam: CriteriaType) => {
const nextCriteria: RatioCriteriaType = [getNumerator(criteria), criteriaParam];
(criteriaParam: PartialCountCriteriaType) => {
const nextCriteria: PartialRatioCriteriaType = [getNumerator(criteria), criteriaParam];
updateCriteria(nextCriteria);
},
[updateCriteria, criteria]
Expand All @@ -150,13 +143,13 @@ const RatioCriteria: React.FC<RatioCriteriaProps> = (props) => {
updateCriterion: updateNumeratorCriterion,
addCriterion: addNumeratorCriterion,
removeCriterion: removeNumeratorCriterion,
} = useCriteriaState(getNumerator(criteria), handleUpdateNumeratorCriteria);
} = useCriteriaState(getNumerator(criteria), defaultCriterion, handleUpdateNumeratorCriteria);

const {
updateCriterion: updateDenominatorCriterion,
addCriterion: addDenominatorCriterion,
removeCriterion: removeDenominatorCriterion,
} = useCriteriaState(getDenominator(criteria), handleUpdateDenominatorCriteria);
} = useCriteriaState(getDenominator(criteria), defaultCriterion, handleUpdateDenominatorCriteria);

return (
<>
Expand Down Expand Up @@ -191,28 +184,17 @@ const RatioCriteria: React.FC<RatioCriteriaProps> = (props) => {
);
};

interface CountCriteriaProps {
alertParams: SharedProps['alertParams'];
fields: SharedProps['fields'];
criteria: CountCriteriaType;
errors: Errors['criteria'];
sourceId: SharedProps['sourceId'];
updateCriteria: (criteria: AlertParams['criteria']) => void;
interface CountCriteriaProps extends SharedProps {
criteria: PartialCountCriteriaType;
}

const CountCriteria: React.FC<CountCriteriaProps> = (props) => {
const { criteria, updateCriteria, errors } = props;

const handleUpdateCriteria = useCallback(
(criteriaParam: CriteriaType) => {
updateCriteria(criteriaParam);
},
[updateCriteria]
);
const { criteria, defaultCriterion, updateCriteria, errors } = props;

const { updateCriterion, addCriterion, removeCriterion } = useCriteriaState(
criteria,
handleUpdateCriteria
defaultCriterion,
updateCriteria
);

return (
Expand All @@ -227,8 +209,9 @@ const CountCriteria: React.FC<CountCriteriaProps> = (props) => {
};

const useCriteriaState = (
criteria: CriteriaType,
onUpdateCriteria: (criteria: CriteriaType) => void
criteria: PartialCountCriteriaType,
defaultCriterion: PartialCriterionType,
onUpdateCriteria: (criteria: PartialCountCriteriaType) => void
) => {
const updateCriterion = useCallback(
(idx, criterionParams) => {
Expand All @@ -241,13 +224,13 @@ const useCriteriaState = (
);

const addCriterion = useCallback(() => {
const nextCriteria = criteria ? [...criteria, DEFAULT_CRITERIA] : [DEFAULT_CRITERIA];
const nextCriteria = [...criteria, defaultCriterion];
onUpdateCriteria(nextCriteria);
}, [criteria, onUpdateCriteria]);
}, [criteria, defaultCriterion, onUpdateCriteria]);

const removeCriterion = useCallback(
(idx) => {
const nextCriteria = criteria.filter((criterion, index) => {
const nextCriteria = criteria.filter((_criterion, index) => {
return index !== idx;
});
onUpdateCriteria(nextCriteria);
Expand Down
Loading