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 datetimeparse Jinja2 template helper filter function #4312

Merged
merged 3 commits into from
May 13, 2024
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 2 additions & 1 deletion docs/sources/configure/jinja2-templating/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -219,9 +219,10 @@ Built-in functions:
- `tojson` - dumps a structure to JSON
- `tojson_pretty` - same as tojson, but prettified
- `iso8601_to_time` - converts time from iso8601 (`2015-02-17T18:30:20.000Z`) to datetime
- `datetimeformat` - converts time from datetime to the given format (`%H:%M / %d-%m-%Y` by default)
- `datetimeformat` - converts datetime to string according to strftime format codes (`%H:%M / %d-%m-%Y` by default)
- `datetimeformat_as_timezone` - same as `datetimeformat`, with the inclusion of timezone conversion (`UTC` by default)
- Usage example: `{{ payload.alerts.startsAt | iso8601_to_time | datetimeformat_as_timezone('%Y-%m-%dT%H:%M:%S%z', 'America/Chicago') }}`
- `datetimeparse` - converts string to datetime according to strftime format codes (`%H:%M / %d-%m-%Y` by default)
- `regex_replace` - performs a regex find and replace
- `regex_match` - performs a regex match, returns `True` or `False`
- Usage example: `{{ payload.ruleName | regex_match(".*") }}`
Expand Down
8 changes: 8 additions & 0 deletions engine/common/jinja_templater/filters.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
import base64
import json
import re
from datetime import datetime

from django.utils.dateparse import parse_datetime
from pytz import timezone


def datetimeparse(value, format="%H:%M / %d-%m-%Y"):
try:
return datetime.strptime(value, format)
except (ValueError, AttributeError, TypeError):
return None


def datetimeformat(value, format="%H:%M / %d-%m-%Y"):
try:
return value.strftime(format)
Expand Down
2 changes: 2 additions & 0 deletions engine/common/jinja_templater/jinja_template_env.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
b64decode,
datetimeformat,
datetimeformat_as_timezone,
datetimeparse,
iso8601_to_time,
json_dumps,
parse_json,
Expand All @@ -25,6 +26,7 @@ def raise_security_exception(name):

jinja_template_env.filters["datetimeformat"] = datetimeformat
jinja_template_env.filters["datetimeformat_as_timezone"] = datetimeformat_as_timezone
jinja_template_env.filters["datetimeparse"] = datetimeparse
jinja_template_env.filters["iso8601_to_time"] = iso8601_to_time
jinja_template_env.filters["tojson_pretty"] = to_pretty_json
jinja_template_env.globals["time"] = timezone.now
Expand Down
43 changes: 43 additions & 0 deletions engine/common/tests/test_apply_jinja_template.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import base64
import json
from datetime import datetime
from unittest.mock import patch

import pytest
Expand Down Expand Up @@ -44,6 +45,14 @@ def test_apply_jinja_template_datetimeformat():
"{{ payload.naive | iso8601_to_time | datetimeformat('%Y-%m-%dT%H:%M:%S%z') }}",
payload,
) == parse_datetime(payload["naive"]).strftime("%Y-%m-%dT%H:%M:%S%z")
assert apply_jinja_template(
"{{ payload.aware | datetimeparse('%Y-%m-%d %H:%M:%S%z') | datetimeformat('%Y-%m-%dT%H:%M:%S%z') }}",
payload,
) == datetime.strptime(payload["aware"], "%Y-%m-%d %H:%M:%S%z").strftime("%Y-%m-%dT%H:%M:%S%z")
assert apply_jinja_template(
"{{ payload.naive | datetimeparse('%Y-%m-%d %H:%M:%S') | datetimeformat('%Y-%m-%dT%H:%M:%S%z') }}",
payload,
) == datetime.strptime(payload["naive"], "%Y-%m-%d %H:%M:%S").strftime("%Y-%m-%dT%H:%M:%S%z")


def test_apply_jinja_template_datetimeformat_as_timezone():
Expand All @@ -57,12 +66,46 @@ def test_apply_jinja_template_datetimeformat_as_timezone():
"{{ payload.naive | iso8601_to_time | datetimeformat_as_timezone('%Y-%m-%dT%H:%M:%S%z', 'America/Chicago') }}",
payload,
) == parse_datetime(payload["naive"]).astimezone(timezone("America/Chicago")).strftime("%Y-%m-%dT%H:%M:%S%z")
assert (
apply_jinja_template(
"""{{ payload.aware | datetimeparse('%Y-%m-%d %H:%M:%S%z') | datetimeformat_as_timezone('%Y-%m-%dT%H:%M:%S%z',
'America/Chicago') }}""",
payload,
)
== parse_datetime(payload["aware"]).astimezone(timezone("America/Chicago")).strftime("%Y-%m-%dT%H:%M:%S%z")
)
assert (
apply_jinja_template(
"""{{ payload.naive | datetimeparse('%Y-%m-%d %H:%M:%S') | datetimeformat_as_timezone('%Y-%m-%dT%H:%M:%S%z',
'America/Chicago') }}""",
payload,
)
== parse_datetime(payload["naive"]).astimezone(timezone("America/Chicago")).strftime("%Y-%m-%dT%H:%M:%S%z")
)

with pytest.raises(JinjaTemplateWarning):
apply_jinja_template(
"{{ payload.aware | iso8601_to_time | datetimeformat_as_timezone('%Y-%m-%dT%H:%M:%S%z', 'potato') }}",
payload,
)
apply_jinja_template(
"""{{ payload.aware | datetimeparse('%Y-%m-%d %H:%M:%S%z') |
datetimeformat_as_timezone('%Y-%m-%dT%H:%M:%S%z', 'potato') }}""",
payload,
)


def test_apply_jinja_template_datetimeparse():
payload = {"aware": "15 05 2024 07:52:11 -0600", "naive": "2024-05-15T07:52:11"}

assert apply_jinja_template(
"{{ payload.aware | datetimeparse('%d %m %Y %H:%M:%S %z') }}",
payload,
) == str(datetime.strptime(payload["aware"], "%d %m %Y %H:%M:%S %z"))
assert apply_jinja_template(
"{{ payload.naive | datetimeparse('%Y-%m-%dT%H:%M:%S') }}",
payload,
) == str(datetime.strptime(payload["naive"], "%Y-%m-%dT%H:%M:%S"))


def test_apply_jinja_template_b64decode():
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ export const genericTemplateCheatSheet: CheatSheetInterface = {
{ listItemName: 'labels - labels assigned to the last alert in the group' },
{ listItemName: 'web_title, web_mesage, web_image_url - templates from Web' },
{ listItemName: 'payload, grafana_oncall_link, grafana_oncall_incident_id, integration_name, source_link' },
{ listItemName: 'time(), datetimeformat, datetimeformat_as_timezone, iso8601_to_time' },
{ listItemName: 'time(), datetimeformat, datetimeformat_as_timezone, datetimeparse, iso8601_to_time' },
{ listItemName: 'to_pretty_json' },
{ listItemName: 'regex_replace, regex_match' },
{ listItemName: 'b64decode' },
Expand Down