add util.dedupe_qs -> dedupe_qs Jinja2 filter

Signed-off-by: Kevin Morris <kevr@0cost.org>
This commit is contained in:
Kevin Morris 2021-06-18 16:57:06 -07:00
parent b1baf76998
commit d5e650a339
3 changed files with 42 additions and 1 deletions

View file

@ -26,6 +26,7 @@ env.filters["tr"] = l10n.tr
# Utility filters.
env.filters["dt"] = util.timestamp_to_datetime
env.filters["as_timezone"] = util.as_timezone
env.filters["dedupe_qs"] = util.dedupe_qs
# Add captcha filters.
env.filters["captcha_salt"] = captcha.captcha_salt_filter

View file

@ -3,8 +3,9 @@ import random
import re
import string
from collections import OrderedDict
from datetime import datetime
from urllib.parse import urlparse
from urllib.parse import quote_plus, urlparse
from zoneinfo import ZoneInfo
from email_validator import EmailNotValidError, EmailUndeliverableError, validate_email
@ -106,6 +107,29 @@ def as_timezone(dt: datetime, timezone: str):
return dt.astimezone(tz=ZoneInfo(timezone))
def dedupe_qs(query_string: str, *additions):
""" Dedupe keys found in a query string by rewriting it without
duplicates found while iterating from the end to the beginning,
using an ordered memo to track keys found and persist locations.
That is, query string 'a=1&b=1&a=2' will be deduped and converted
to 'b=1&a=2'.
:param query_string: An HTTP URL query string.
:param *additions: Optional additional fields to add to the query string.
:return: Deduped query string, including *additions at the tail.
"""
for addition in list(additions):
query_string += f"&{addition}"
qs = OrderedDict()
for item in reversed(query_string.split('&')):
key, value = item.split('=')
if key not in qs:
qs[key] = value
return '&'.join([f"{k}={quote_plus(v)}" for k, v in reversed(qs.items())])
def jsonify(obj):
""" Perform a conversion on obj if it's needed. """
if isinstance(obj, datetime):

View file

@ -1,3 +1,4 @@
from collections import OrderedDict
from datetime import datetime
from zoneinfo import ZoneInfo
@ -14,3 +15,18 @@ def test_as_timezone():
ts = datetime.utcnow().timestamp()
dt = util.timestamp_to_datetime(ts)
assert util.as_timezone(dt, "UTC") == dt.astimezone(tz=ZoneInfo("UTC"))
def test_dedupe_qs():
items = OrderedDict()
items["key1"] = "test"
items["key2"] = "blah"
items["key3"] = 1
# Construct and test our query string.
query_string = '&'.join([f"{k}={v}" for k, v in items.items()])
assert query_string == "key1=test&key2=blah&key3=1"
# Add key1=changed and key2=changed to the query and dedupe it.
deduped = util.dedupe_qs(query_string, "key1=changed", "key3=changed")
assert deduped == "key2=blah&key1=changed&key3=changed"