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
[Alerting] Corrects validation and errors handling in PagerDuty action #63954
Changes from all commits
ffc0c29
34d0166
68359d0
e7aa47d
d36324b
f1d1cf7
4f2e734
02451b8
bfcd018
633f20f
a1df80f
5131cb2
b4a821e
28ac631
baab614
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,6 +14,7 @@ import { | |
} from '@elastic/eui'; | ||
import { i18n } from '@kbn/i18n'; | ||
import { FormattedMessage } from '@kbn/i18n/react'; | ||
import moment from 'moment'; | ||
import { | ||
ActionTypeModel, | ||
ActionConnectorFieldsProps, | ||
|
@@ -23,6 +24,7 @@ import { | |
import { PagerDutyActionParams, PagerDutyActionConnector } from './types'; | ||
import pagerDutySvg from './pagerduty.svg'; | ||
import { AddMessageVariables } from '../add_message_variables'; | ||
import { hasMustacheTokens } from '../../lib/has_mustache_tokens'; | ||
|
||
export function getActionType(): ActionTypeModel { | ||
return { | ||
|
@@ -62,6 +64,7 @@ export function getActionType(): ActionTypeModel { | |
const validationResult = { errors: {} }; | ||
const errors = { | ||
summary: new Array<string>(), | ||
timestamp: new Array<string>(), | ||
}; | ||
validationResult.errors = errors; | ||
if (!actionParams.summary?.length) { | ||
|
@@ -74,6 +77,24 @@ export function getActionType(): ActionTypeModel { | |
) | ||
); | ||
} | ||
if (actionParams.timestamp && !hasMustacheTokens(actionParams.timestamp)) { | ||
if (isNaN(Date.parse(actionParams.timestamp))) { | ||
const { nowShortFormat, nowLongFormat } = getValidTimestampExamples(); | ||
errors.timestamp.push( | ||
i18n.translate( | ||
'xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.error.invalidTimestamp', | ||
{ | ||
defaultMessage: | ||
'Timestamp must be a valid date, such as {nowShortFormat} or {nowLongFormat}.', | ||
values: { | ||
nowShortFormat, | ||
nowLongFormat, | ||
}, | ||
} | ||
) | ||
); | ||
} | ||
} | ||
return validationResult; | ||
}, | ||
actionConnectorFields: PagerDutyActionConnectorFields, | ||
|
@@ -334,6 +355,8 @@ const PagerDutyParamsFields: React.FunctionComponent<ActionParamsProps<PagerDuty | |
<EuiFlexItem> | ||
<EuiFormRow | ||
fullWidth | ||
error={errors.timestamp} | ||
isInvalid={errors.timestamp.length > 0 && timestamp !== undefined} | ||
label={i18n.translate( | ||
'xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.timestampTextFieldLabel', | ||
{ | ||
|
@@ -355,11 +378,14 @@ const PagerDutyParamsFields: React.FunctionComponent<ActionParamsProps<PagerDuty | |
name="timestamp" | ||
data-test-subj="timestampInput" | ||
value={timestamp || ''} | ||
isInvalid={errors.timestamp.length > 0 && timestamp !== undefined} | ||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => { | ||
editAction('timestamp', e.target.value, index); | ||
}} | ||
onBlur={() => { | ||
if (!timestamp) { | ||
if (timestamp?.trim()) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was wondering about doing the trim() in the action params validation rather than in the UI here. Guessing here fixes it 99% of the time, but are there cases where it could be missed here, and still have leading/trailing whitespace when the validation occurs? Guessing possible, but very unlikely. In any case, the validation now prints the date value it's validating with dq's, so ... think this will be user-diagnose-able now where it wasn't before ... There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah I originally wanted to do it in the action, but that forces us to do trim in multiple places as the validation is separate from where its used and it ends up forcing the It would also mean it's very easy for us to think there's a timestamp value (as the SO wouldn't be null or even an empty string) but in practice we'd be treating it like we don't have one. Seems error prone. |
||
editAction('timestamp', timestamp.trim(), index); | ||
} else { | ||
editAction('timestamp', '', index); | ||
} | ||
}} | ||
|
@@ -534,3 +560,11 @@ const PagerDutyParamsFields: React.FunctionComponent<ActionParamsProps<PagerDuty | |
</Fragment> | ||
); | ||
}; | ||
|
||
function getValidTimestampExamples() { | ||
const now = moment(); | ||
return { | ||
nowShortFormat: now.format('YYYY-MM-DD'), | ||
nowLongFormat: now.format('YYYY-MM-DD h:mm:ss'), | ||
}; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License; | ||
* you may not use this file except in compliance with the Elastic License. | ||
*/ | ||
|
||
import uuid from 'uuid'; | ||
|
||
import { hasMustacheTokens } from './has_mustache_tokens'; | ||
|
||
describe('hasMustacheTokens', () => { | ||
test('returns false for empty string', () => { | ||
expect(hasMustacheTokens('')).toBe(false); | ||
}); | ||
|
||
test('returns false for string without tokens', () => { | ||
expect(hasMustacheTokens(`some random string ${uuid.v4()}`)).toBe(false); | ||
}); | ||
|
||
test('returns true when a template token is present', () => { | ||
expect(hasMustacheTokens('{{context.timestamp}}')).toBe(true); | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License; | ||
* you may not use this file except in compliance with the Elastic License. | ||
*/ | ||
|
||
export function hasMustacheTokens(str: string): boolean { | ||
return null !== str.match(/{{.*}}/); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems like it's parsing the action params to see if the date field doesn't have a mustache template, and then parsing it to see if it's a valid date. That makes sense, but I wonder how often people will be setting literal dates in their action parameters like this. Is it worth it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, we've already had a bug reported for not doing it so I get the feeling our UX isn't really good enough here.
If a specific format is required for this field, we should be able to validate that only content of that format is defined for it. The fact that we also want to support Mustache shouldn't mean the user can input bad data here... arguably we shouldn't allow any free values here at all, but rather we should only offer valid sources of Datetime strings, but when I looked into that I realised that was a huge change that didn't feel right as a patch bug fix. 🤷