Jinja2 template helper filter datetimeformat_as_timezone (#3426)
# What this PR does Add an additional jinja2 template helper filter to convert a timezone aware datetime to a different timezone. ## Which issue(s) this PR fixes Alert payloads that originate from different time zones may include timestamps having a local time offset. This filter enables standardization of timestamp timezones. ## Checklist - [x] Unit, integration, and e2e (if applicable) tests updated - [x] Documentation added (or `pr:no public docs` PR label added if not required) - [x] `CHANGELOG.md` updated (or `pr:no changelog` PR label added if not required) --------- Co-authored-by: Joey Orlando <joey.orlando@grafana.com>
This commit is contained in:
parent
e39baa6bbe
commit
4df8985283
7 changed files with 78 additions and 10 deletions
|
|
@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
|
||||
## Unreleased
|
||||
|
||||
### Added
|
||||
|
||||
- Add `datetimeformat_as_timezone` Jinja2 template helper filter by @jorgeav ([#3426](https://github.com/grafana/oncall/pull/3426))
|
||||
|
||||
### Changed
|
||||
|
||||
- Disallow creating and deleting direct paging integrations by @vadimkerr ([#3475](https://github.com/grafana/oncall/pull/3475))
|
||||
|
|
|
|||
|
|
@ -205,8 +205,12 @@ Built-in functions:
|
|||
- `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_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') }}`
|
||||
- `regex_replace` - performs a regex find and replace
|
||||
- `regex_match` - performs a regex match, returns `True` or `False`. Usage example: `{{ payload.ruleName | regex_match(".*") }}`
|
||||
- `b64decode` - performs a base64 string decode. Usage example: `{{ payload.data | b64decode }}`
|
||||
- `regex_match` - performs a regex match, returns `True` or `False`
|
||||
- Usage example: `{{ payload.ruleName | regex_match(".*") }}`
|
||||
- `b64decode` - performs a base64 string decode
|
||||
- Usage example: `{{ payload.data | b64decode }}`
|
||||
|
||||
{{< section >}}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import json
|
|||
import re
|
||||
|
||||
from django.utils.dateparse import parse_datetime
|
||||
from pytz import timezone
|
||||
|
||||
|
||||
def datetimeformat(value, format="%H:%M / %d-%m-%Y"):
|
||||
|
|
@ -12,6 +13,13 @@ def datetimeformat(value, format="%H:%M / %d-%m-%Y"):
|
|||
return None
|
||||
|
||||
|
||||
def datetimeformat_as_timezone(value, format="%H:%M / %d-%m-%Y", tz="UTC"):
|
||||
try:
|
||||
return value.astimezone(timezone(tz)).strftime(format)
|
||||
except (ValueError, AttributeError, TypeError):
|
||||
return None
|
||||
|
||||
|
||||
def iso8601_to_time(value):
|
||||
try:
|
||||
return parse_datetime(value)
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ from jinja2.sandbox import SandboxedEnvironment
|
|||
from .filters import (
|
||||
b64decode,
|
||||
datetimeformat,
|
||||
datetimeformat_as_timezone,
|
||||
iso8601_to_time,
|
||||
json_dumps,
|
||||
regex_match,
|
||||
|
|
@ -22,6 +23,7 @@ def raise_security_exception(name):
|
|||
jinja_template_env = SandboxedEnvironment(loader=BaseLoader())
|
||||
|
||||
jinja_template_env.filters["datetimeformat"] = datetimeformat
|
||||
jinja_template_env.filters["datetimeformat_as_timezone"] = datetimeformat_as_timezone
|
||||
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
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
import base64
|
||||
import json
|
||||
|
||||
import pytest
|
||||
from django.conf import settings
|
||||
from django.utils.dateparse import parse_datetime
|
||||
from pytz import timezone
|
||||
|
||||
from common.jinja_templater import apply_jinja_template
|
||||
from common.jinja_templater.apply_jinja_template import JinjaTemplateError, JinjaTemplateWarning
|
||||
|
|
@ -14,6 +17,60 @@ def test_apply_jinja_template():
|
|||
assert payload == result
|
||||
|
||||
|
||||
def test_apply_jinja_template_iso8601_to_time():
|
||||
payload = {"name": "2023-11-22T15:30:00.000000000Z"}
|
||||
|
||||
result = apply_jinja_template(
|
||||
"{{ payload.name | iso8601_to_time }}",
|
||||
payload,
|
||||
)
|
||||
expected = str(parse_datetime(payload["name"]))
|
||||
assert result == expected
|
||||
|
||||
|
||||
def test_apply_jinja_template_datetimeformat():
|
||||
payload = {"aware": "2023-05-28 23:11:12+0000", "naive": "2023-05-28 23:11:12"}
|
||||
|
||||
assert apply_jinja_template(
|
||||
"{{ payload.aware | iso8601_to_time | datetimeformat('%Y-%m-%dT%H:%M:%S%z') }}",
|
||||
payload,
|
||||
) == parse_datetime(payload["aware"]).strftime("%Y-%m-%dT%H:%M:%S%z")
|
||||
assert apply_jinja_template(
|
||||
"{{ 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")
|
||||
|
||||
|
||||
def test_apply_jinja_template_datetimeformat_as_timezone():
|
||||
payload = {"aware": "2023-05-28 23:11:12+0000", "naive": "2023-05-28 23:11:12"}
|
||||
|
||||
assert apply_jinja_template(
|
||||
"{{ payload.aware | iso8601_to_time | 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 | 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")
|
||||
|
||||
with pytest.raises(JinjaTemplateWarning):
|
||||
apply_jinja_template(
|
||||
"{{ payload.aware | iso8601_to_time | datetimeformat_as_timezone('%Y-%m-%dT%H:%M:%S%z', 'potato') }}",
|
||||
payload,
|
||||
)
|
||||
|
||||
|
||||
def test_apply_jinja_template_b64decode():
|
||||
payload = {"name": "SGVsbG8sIHdvcmxkIQ=="}
|
||||
|
||||
assert apply_jinja_template(
|
||||
"{{ payload.name | b64decode }}",
|
||||
payload,
|
||||
) == base64.b64decode(
|
||||
payload["name"]
|
||||
).decode("utf-8")
|
||||
|
||||
|
||||
def test_apply_jinja_template_json_dumps():
|
||||
payload = {"name": "test"}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +0,0 @@
|
|||
from common.jinja_templater.filters import b64decode
|
||||
|
||||
|
||||
def test_base64_decode():
|
||||
original = "dGVzdCBzdHJpbmch"
|
||||
expected = "test string!"
|
||||
assert b64decode(original) == expected
|
||||
|
|
@ -80,7 +80,7 @@ export const genericTemplateCheatSheet: CheatSheetInterface = {
|
|||
{ listItemName: 'payload - payload of 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, iso8601_to_time' },
|
||||
{ listItemName: 'time(), datetimeformat, datetimeformat_as_timezone, iso8601_to_time' },
|
||||
{ listItemName: 'to_pretty_json' },
|
||||
{ listItemName: 'regex_replace, regex_match' },
|
||||
{ listItemName: 'b64decode' },
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue