Add doc references to regex_search jinja filter (#4973)
This filter wasn't listed in cheatsheet or docs, but it is usually more flexible/simpler than `regex_match` if trying to search for a substring in payload. Also setup a [timeout](https://github.com/mrabarnett/mrab-regex?tab=readme-ov-file#timeout) when regex searching/matching/replacing. Related to [this issue](https://docs.google.com/document/d/1gESMLdbJSnLnSnK7Nhp7DvJ7f10qZXsEm5GrgBc5RqQ/edit)
This commit is contained in:
parent
de40bbbc6a
commit
0fa3522db7
7 changed files with 75 additions and 14 deletions
|
|
@ -95,6 +95,8 @@ Grafana OnCall enhances Jinja with additional functions:
|
|||
- `regex_replace`: Performs a regex find and replace
|
||||
- `regex_match`: Performs a regex match, returns `True` or `False`
|
||||
- Usage example: `{{ payload.ruleName | regex_match(".*") }}`
|
||||
- `regex_search`: Performs a regex search, returns `True` or `False`
|
||||
- Usage example: `{{ payload.message | regex_search("Severity: (High|Critical)") }}`
|
||||
- `b64decode`: Performs a base64 string decode
|
||||
- Usage example: `{{ payload.data | b64decode }}`
|
||||
- `parse_json`:Parses a JSON string to an object
|
||||
|
|
|
|||
|
|
@ -1,11 +1,13 @@
|
|||
import base64
|
||||
import json
|
||||
import re
|
||||
from datetime import datetime
|
||||
|
||||
import regex
|
||||
from django.utils.dateparse import parse_datetime
|
||||
from pytz import timezone
|
||||
|
||||
REGEX_TIMEOUT = 2
|
||||
|
||||
|
||||
def datetimeparse(value, format="%H:%M / %d-%m-%Y"):
|
||||
try:
|
||||
|
|
@ -52,22 +54,22 @@ def json_dumps(value):
|
|||
|
||||
def regex_replace(value, find, replace):
|
||||
try:
|
||||
return re.sub(find, replace, value)
|
||||
except (ValueError, AttributeError, TypeError):
|
||||
return regex.sub(find, replace, value, timeout=REGEX_TIMEOUT)
|
||||
except (ValueError, AttributeError, TypeError, TimeoutError):
|
||||
return None
|
||||
|
||||
|
||||
def regex_match(pattern, value):
|
||||
try:
|
||||
return bool(re.match(value, pattern))
|
||||
except (ValueError, AttributeError, TypeError):
|
||||
return bool(regex.match(value, pattern, timeout=REGEX_TIMEOUT))
|
||||
except (ValueError, AttributeError, TypeError, TimeoutError):
|
||||
return None
|
||||
|
||||
|
||||
def regex_search(pattern, value):
|
||||
try:
|
||||
return bool(re.search(value, pattern))
|
||||
except (ValueError, AttributeError, TypeError):
|
||||
return bool(regex.search(value, pattern, timeout=REGEX_TIMEOUT))
|
||||
except (ValueError, AttributeError, TypeError, TimeoutError):
|
||||
return None
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,38 @@ from common.jinja_templater.apply_jinja_template import (
|
|||
templated_value_is_truthy,
|
||||
)
|
||||
|
||||
EMAIL_SAMPLE_PAYLOAD = {
|
||||
"subject": "[Reminder] Review GKE getServerConfig API permission changes",
|
||||
"message": "Hello Google Kubernetes Customer,\r\n"
|
||||
"\r\n"
|
||||
"We’re writing to remind you that starting October 22, 2024, "
|
||||
"the \r\n"
|
||||
"getServerConfig API for Google Kubernetes Engine (GKE) will "
|
||||
"enforce \r\n"
|
||||
"Identity and Access Management (IAM) container.clusters.list "
|
||||
"checks. This \r\n"
|
||||
"change follows a series of security improvements as IAM \r\n"
|
||||
"container.clusters.list permissions are being enforced across "
|
||||
"the \r\n"
|
||||
"getServerConfig API.\r\n"
|
||||
"\r\n"
|
||||
"We’ve provided additional information below to guide you through "
|
||||
"this \r\n"
|
||||
"change.\r\n"
|
||||
"\r\n"
|
||||
"What you need to know\r\n"
|
||||
"\r\n"
|
||||
"The current implementation doesn’t apply a specific permissions "
|
||||
"check via \r\n"
|
||||
"getServerConfig API. After this change goes into effect for the "
|
||||
"Google \r\n"
|
||||
"Kubernetes Engine API getServerConfig, only authorized users with "
|
||||
"the \r\n"
|
||||
"container.clusters.list permissions will be able to call the \r\n"
|
||||
"GetServerConfig.\r\n",
|
||||
"sender": "someone@somewhere.dev",
|
||||
}
|
||||
|
||||
|
||||
def test_apply_jinja_template():
|
||||
payload = {"name": "test"}
|
||||
|
|
@ -127,25 +159,49 @@ def test_apply_jinja_template_json_dumps():
|
|||
assert result == expected
|
||||
|
||||
|
||||
@pytest.mark.filterwarnings("ignore:::jinja2.*") # ignore regex escape sequence warning
|
||||
def test_apply_jinja_template_regex_match():
|
||||
payload = {"name": "test"}
|
||||
payload = {
|
||||
"name": "test",
|
||||
"message": json.dumps(EMAIL_SAMPLE_PAYLOAD),
|
||||
}
|
||||
|
||||
assert apply_jinja_template("{{ payload.name | regex_match('.*') }}", payload) == "True"
|
||||
assert apply_jinja_template("{{ payload.name | regex_match('tes') }}", payload) == "True"
|
||||
assert apply_jinja_template("{{ payload.name | regex_match('test1') }}", payload) == "False"
|
||||
# check for timeouts
|
||||
with patch("common.jinja_templater.filters.REGEX_TIMEOUT", 1):
|
||||
assert (
|
||||
apply_jinja_template(
|
||||
"{{ payload.message | regex_match('(.|\\s)+Severity(.|\\s){2}High(.|\\s)+') }}", payload
|
||||
)
|
||||
== "False"
|
||||
)
|
||||
|
||||
# Check that exception is raised when regex is invalid
|
||||
with pytest.raises(JinjaTemplateError):
|
||||
apply_jinja_template("{{ payload.name | regex_match('*') }}", payload)
|
||||
|
||||
|
||||
@pytest.mark.filterwarnings("ignore:::jinja2.*") # ignore regex escape sequence warning
|
||||
def test_apply_jinja_template_regex_search():
|
||||
payload = {"name": "test"}
|
||||
payload = {
|
||||
"name": "test",
|
||||
"message": json.dumps(EMAIL_SAMPLE_PAYLOAD),
|
||||
}
|
||||
|
||||
assert apply_jinja_template("{{ payload.name | regex_search('.*') }}", payload) == "True"
|
||||
assert apply_jinja_template("{{ payload.name | regex_search('tes') }}", payload) == "True"
|
||||
assert apply_jinja_template("{{ payload.name | regex_search('est') }}", payload) == "True"
|
||||
assert apply_jinja_template("{{ payload.name | regex_search('test1') }}", payload) == "False"
|
||||
# check for timeouts
|
||||
with patch("common.jinja_templater.filters.REGEX_TIMEOUT", 1):
|
||||
assert (
|
||||
apply_jinja_template(
|
||||
"{{ payload.message | regex_search('(.|\\s)+Severity(.|\\s){2}High(.|\\s)+') }}", payload
|
||||
)
|
||||
== "False"
|
||||
)
|
||||
|
||||
# Check that exception is raised when regex is invalid
|
||||
with pytest.raises(JinjaTemplateError):
|
||||
|
|
|
|||
|
|
@ -71,6 +71,7 @@ module = [
|
|||
"polymorphic.*",
|
||||
"pyroscope.*",
|
||||
"ratelimit.*",
|
||||
"regex.*",
|
||||
"recurring_ical_events.*",
|
||||
"rest_polymorphic.*",
|
||||
"slackclient.*",
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ PyMySQL==1.1.1
|
|||
python-telegram-bot==13.13
|
||||
recurring-ical-events==2.1.0
|
||||
redis==5.0.1
|
||||
regex==2021.11.2
|
||||
regex==2024.7.24
|
||||
requests==2.32.3
|
||||
slack-export-viewer==1.1.4
|
||||
slack_sdk==3.21.3
|
||||
|
|
|
|||
|
|
@ -393,7 +393,7 @@ referencing==0.33.0
|
|||
# via
|
||||
# jsonschema
|
||||
# jsonschema-specifications
|
||||
regex==2021.11.2
|
||||
regex==2024.7.24
|
||||
# via -r engine/requirements.in
|
||||
requests==2.32.3
|
||||
# via
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ export const groupingTemplateCheatSheet: CheatSheetInterface = {
|
|||
name: 'Additional variables and functions',
|
||||
listItems: [
|
||||
{ listItemName: 'time(), datetimeformat, iso8601_to_time' },
|
||||
{ listItemName: 'regex_replace, regex_match' },
|
||||
{ listItemName: 'regex_replace, regex_match, regex_search' },
|
||||
],
|
||||
},
|
||||
{
|
||||
|
|
@ -86,7 +86,7 @@ export const genericTemplateCheatSheet: CheatSheetInterface = {
|
|||
{ listItemName: 'payload, grafana_oncall_link, grafana_oncall_incident_id, integration_name, source_link' },
|
||||
{ listItemName: 'time(), datetimeformat, datetimeformat_as_timezone, datetimeparse, iso8601_to_time' },
|
||||
{ listItemName: 'to_pretty_json' },
|
||||
{ listItemName: 'regex_replace, regex_match' },
|
||||
{ listItemName: 'regex_replace, regex_match, regex_search' },
|
||||
{ listItemName: 'b64decode' },
|
||||
],
|
||||
},
|
||||
|
|
@ -143,7 +143,7 @@ export const slackMessageTemplateCheatSheet: CheatSheetInterface = {
|
|||
{ listItemName: 'payload, grafana_oncall_link, grafana_oncall_incident_id, integration_name, source_link' },
|
||||
{ listItemName: 'time(), datetimeformat, iso8601_to_time' },
|
||||
{ listItemName: 'to_pretty_json' },
|
||||
{ listItemName: 'regex_replace, regex_match' },
|
||||
{ listItemName: 'regex_replace, regex_match, regex_search' },
|
||||
],
|
||||
},
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue