Skip to content

Commit

Permalink
Merge pull request #86 from Lightmatter/feature/uuid-parsing
Browse files Browse the repository at this point in the history
Add support for UUIDs in request payloads
  • Loading branch information
samamorgan authored Sep 19, 2023
2 parents 3aaf8b7 + 8e399d9 commit ab9bfe2
Show file tree
Hide file tree
Showing 3 changed files with 185 additions and 110 deletions.
52 changes: 52 additions & 0 deletions test/conftest.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import json
import os
import uuid
from datetime import date, datetime, time, timedelta, timezone
from uuid import uuid4

import pytest

Expand Down Expand Up @@ -103,3 +105,53 @@ def hook(dct):
return dct

return hook


@pytest.fixture
def base_date():
return date(year=2022, month=9, day=15)


@pytest.fixture
def base_date_str():
return "2022-09-15T00:00:00.000Z"


@pytest.fixture
def base_time():
return time(hour=23, minute=0, second=0, microsecond=0)


@pytest.fixture
def utc_datetime(base_date, base_time):
return datetime.combine(base_date, base_time, tzinfo=timezone.utc)


@pytest.fixture
def utc_datetime_str():
return "2022-09-15T23:00:00.000Z"


@pytest.fixture
def pst_datetime(base_date, base_time):
return datetime.combine(base_date, base_time, tzinfo=timezone(timedelta(hours=-8)))


@pytest.fixture
def pst_datetime_str():
return "2022-09-16T07:00:00.000Z"


@pytest.fixture
def est_datetime(base_date, base_time):
return datetime.combine(base_date, base_time, tzinfo=timezone(timedelta(hours=-5)))


@pytest.fixture
def est_datetime_str():
return "2022-09-16T04:00:00.000Z"


@pytest.fixture
def _uuid():
return uuid4()
195 changes: 105 additions & 90 deletions test/test_util.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import copy
import sys
from datetime import date, datetime, timedelta, timezone

import pytest

from welkin.util import (
clean_date,
Expand All @@ -9,93 +11,106 @@
clean_request_payload,
)

UTC = timezone.utc
PST = timezone(timedelta(hours=-8))
EST = timezone(timedelta(hours=-5))


def test_clean_request_payload():
payload = {
"datetime": datetime(2022, 9, 15, 23, 0, 0, 0, PST),
"date": date(2022, 9, 15),
"dict": {"foo": "bar", "nested": {"date": date(2022, 9, 15)}},
"list": [datetime(2022, 9, 15, 23, 0, 0, 0, UTC)],
"str": "Don't clean me please.",
"int": [-sys.maxsize, 0, sys.maxsize],
"float": [sys.float_info.min, 1, sys.float_info.max],
"bool": [True, False],
"none": None,
}
payload_copy = dict(payload)

cleaned = clean_request_payload(payload)
assert payload == payload_copy, "Payload was modified"

assert cleaned["datetime"] == "2022-09-16T07:00:00.000Z"
assert cleaned["date"] == "2022-09-15T00:00:00.000Z"
assert cleaned["dict"]["nested"]["date"] == "2022-09-15T00:00:00.000Z"
assert cleaned["list"][0] == "2022-09-15T23:00:00.000Z"


def test_clean_json_list():
json_list = [
datetime(2022, 9, 15, 23, 0, 0, 0, UTC),
date(2022, 9, 15),
[
{
"foo": [datetime(2022, 9, 15, 23, 0, 0, 0, PST)],

class TestCleanRequestPayload:
@pytest.fixture
def payload(self, _uuid, base_date, utc_datetime, pst_datetime):
return {
"datetime": pst_datetime,
"date": base_date,
"dict": {
"foo": "bar",
"nested": {"date": base_date},
},
],
"Strings don't get cleaned",
]
json_list_copy = list(json_list)

cleaned = clean_json_list(json_list)
assert json_list == json_list_copy, "JSON list was modified"

assert cleaned[0] == "2022-09-15T23:00:00.000Z"
assert cleaned[1] == "2022-09-15T00:00:00.000Z"
assert cleaned[2][0]["foo"][0] == "2022-09-16T07:00:00.000Z"


def test_clean_request_params():
params = {
"datetime": datetime(2022, 9, 15, 23, 0, 0, 0, PST),
"date": date(2022, 9, 15),
"list": ["foo", "bar", "baz"],
}
params_copy = dict(params)
cleaned = clean_request_params(params)

assert params == params_copy, "Parameter dict was modified"

assert cleaned["datetime"] == "2022-09-16T07:00:00.000Z"
assert cleaned["date"] == "2022-09-15T00:00:00.000Z"
assert cleaned["list"] == "foo,bar,baz"


def test_clean_date():
cleaned = clean_date(date(2022, 9, 15))

assert cleaned == "2022-09-15T00:00:00.000Z"


def test_clean_datetime():
datetime_tests = {
"utc": {
"dt": datetime(2022, 9, 15, 23, 0, 0, 0, UTC),
"expected": "2022-09-15T23:00:00.000Z",
},
"pst": {
"dt": datetime(2022, 9, 15, 23, 0, 0, 0, PST),
"expected": "2022-09-16T07:00:00.000Z",
},
"est": {
"dt": datetime(2022, 9, 15, 23, 0, 0, 0, EST),
"expected": "2022-09-16T04:00:00.000Z",
},
}
timedelta()
for name, data in datetime_tests.items():
cleaned = clean_datetime(data["dt"])
assert cleaned == data["expected"], f"Unexpected result for '{name}'"
"list": [utc_datetime],
"str": "Don't clean me please.",
"int": [-sys.maxsize, 0, sys.maxsize],
"float": [sys.float_info.min, 1, sys.float_info.max],
"bool": [True, False],
"_uuid4": _uuid,
"none": None,
}

def test_clean_request_payload(
self, payload, _uuid, pst_datetime_str, base_date_str, utc_datetime_str
):
payload_copy = copy.deepcopy(payload)
cleaned = clean_request_payload(payload)
assert payload == payload_copy, "Payload was modified"

assert cleaned["datetime"] == pst_datetime_str
assert cleaned["date"] == base_date_str
assert cleaned["dict"]["nested"]["date"] == base_date_str
assert cleaned["list"][0] == utc_datetime_str
assert cleaned["_uuid4"] == str(_uuid)


class TestCleanJsonList:
@pytest.fixture
def json_list(self, utc_datetime, base_date, pst_datetime):
return [
utc_datetime,
base_date,
[
{
"foo": [pst_datetime],
},
],
"Strings don't get cleaned",
]

def test_clean_json_list(
self, json_list, utc_datetime_str, base_date_str, pst_datetime_str
):
json_list_copy = copy.deepcopy(json_list)
cleaned = clean_json_list(json_list)
assert json_list == json_list_copy, "JSON list was modified"

assert cleaned[0] == utc_datetime_str
assert cleaned[1] == base_date_str
assert cleaned[2][0]["foo"][0] == pst_datetime_str


class TestCleanRequestParams:
@pytest.fixture
def params(self, pst_datetime, base_date):
return {
"datetime": pst_datetime,
"date": base_date,
"list": ["foo", "bar", "baz"],
}

def test_clean_request_params(self, params, pst_datetime_str, base_date_str):
params_copy = copy.deepcopy(params)
cleaned = clean_request_params(params)
assert params == params_copy, "Parameter dict was modified"

assert cleaned["datetime"] == pst_datetime_str
assert cleaned["date"] == base_date_str
assert cleaned["list"] == "foo,bar,baz"


def test_clean_date(base_date, base_date_str):
assert clean_date(base_date) == base_date_str


@pytest.mark.parametrize(
"dt,expected",
[
(
"utc_datetime",
"utc_datetime_str",
),
(
"pst_datetime",
"pst_datetime_str",
),
(
"est_datetime",
"est_datetime_str",
),
],
)
def test_clean_datetime(dt, expected, request):
cleaned = clean_datetime(request.getfixturevalue(dt))
assert cleaned == request.getfixturevalue(expected)
48 changes: 28 additions & 20 deletions welkin/util.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
from datetime import date, datetime, timezone
from typing import Any
from uuid import UUID

from welkin.models.base import SchemaBase

Expand Down Expand Up @@ -38,36 +40,42 @@ def _build_resources(instance: type, attribute_name: str, value: type = None) ->
setattr(val, attribute_name, value)


def clean_data(value: Any) -> Any:
"""Clean data for JSON serialization.
Args:
value (Any): The value to clean.
Returns:
Any: The cleaned value.
"""
if isinstance(value, datetime):
return clean_datetime(value)
elif isinstance(value, date):
return clean_date(value)
elif isinstance(value, dict):
return clean_request_payload(value)
elif isinstance(value, list):
return clean_json_list(value)
elif isinstance(value, UUID):
return str(value)

# No cleaning needed
return value


def clean_request_payload(payload: dict) -> dict:
result = {}
for k, v in payload.items():
if isinstance(v, datetime):
result[k] = clean_datetime(v)
elif isinstance(v, date):
result[k] = clean_date(v)
elif isinstance(v, dict):
result[k] = clean_request_payload(v)
elif isinstance(v, list):
result[k] = clean_json_list(v)
else:
result[k] = v
result[k] = clean_data(v)

return result


def clean_json_list(data: list) -> list:
result = []
for item in data:
if isinstance(item, datetime):
result.append(clean_datetime(item))
elif isinstance(item, date):
result.append(clean_date(item))
elif isinstance(item, dict):
result.append(clean_request_payload(item))
elif isinstance(item, list):
result.append(clean_json_list(item))
else:
result.append(item)
result.append(clean_data(item))

return result

Expand Down

0 comments on commit ab9bfe2

Please sign in to comment.