Skip to content
This repository has been archived by the owner on Oct 30, 2022. It is now read-only.

Commit

Permalink
Add serializer methods to HttpExchangeWriter. (#33)
Browse files Browse the repository at this point in the history
* Add to_json and to_dict methods to writer.

* Add tests, update readme.
  • Loading branch information
Kimmo Sääskilahti committed Mar 19, 2020
1 parent 75aa505 commit 86e5c19
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 15 deletions.
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ pip install http-types
```

## Writing HTTP exchanges

Using `HttpExchangeWriter`a recording of HTTP traffic can be serialised for use with any program that can handle the HTTP Types format:

```python
Expand All @@ -37,9 +38,15 @@ exchange = HttpExchange(request=request, response=response)
with tempfile.TemporaryFile(mode="w") as output:
writer = HttpExchangeWriter(output)
writer.write(exchange)

# Serialize to dictionary
as_dict = HttpExchangeWriter.to_dict(exchange)
# Serialize to JSON string
as_str = HttpExchangeWriter.to_json(exchange)
```

## Reading HTTP exchanges

With `HttpExchangeReader` recordings in the HTTP Types format can be read for processing:

```python
Expand All @@ -50,6 +57,7 @@ for exchange in HttpExchangeReader.from_jsonl(input_file):
```

## Development

Initial setup:

1. Create a new virtual environment.
Expand Down
41 changes: 26 additions & 15 deletions http_types/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from http.client import HTTPResponse
from dateutil.parser import isoparse
import copy
from dataclasses import asdict, is_dataclass
from dataclasses import asdict
from http_types.types import (
HttpMethod,
Protocol,
Expand All @@ -18,6 +18,8 @@
import re
from urllib import request

HttpType = Union[HttpExchange, Request, Response]

__all__ = [
"RequestBuilder",
"ResponseBuilder",
Expand Down Expand Up @@ -85,24 +87,27 @@ def parse_qs_flattening(query_string: str) -> Query:
return query_dict


def fixup_entries_for_serialization(data_to_be_serialized):
def fixup_entries_for_serialization(data_to_be_serialized: Union[Dict, HttpType]):
"""Fixup entries for JSON serialization"""
result = (
asdict(data_to_be_serialized)
if is_dataclass(data_to_be_serialized)
else copy.deepcopy(data_to_be_serialized)
as_dict = (
data_to_be_serialized
if isinstance(data_to_be_serialized, dict)
else asdict(data_to_be_serialized)
)
to_iter = copy.deepcopy(result)
# Deep copy to avoid mutating nested dictionaries
as_dict = copy.deepcopy(as_dict)
# Copy for iteration
to_iter = copy.deepcopy(as_dict)
for key, value in to_iter.items():
if key == "bodyAsJson" or value is None or value == "":
del result[key]
del as_dict[key]
elif key == "method":
result[key] = result[key].value
as_dict[key] = as_dict[key].value
elif key == "protocol":
result[key] = result[key].value
as_dict[key] = as_dict[key].value
elif isinstance(value, dict):
result[key] = fixup_entries_for_serialization(value)
return result
as_dict[key] = fixup_entries_for_serialization(value)
return as_dict


def parse_iso860_datetime(input_string: str) -> datetime:
Expand Down Expand Up @@ -408,7 +413,13 @@ def write(self, exchange: HttpExchange):
Arguments:
exchange: {HttpExchange} -- The exchange to write.
"""
json.dump(
fixup_entries_for_serialization(exchange), self.output, default=json_serial
)
json.dump(self.to_dict(exchange), self.output, default=json_serial)
self.output.write("\n")

@staticmethod
def to_dict(obj: HttpType) -> dict:
return fixup_entries_for_serialization(obj)

@staticmethod
def to_json(obj: HttpType) -> str:
return json.dumps(HttpExchangeWriter.to_dict(obj), default=json_serial)
31 changes: 31 additions & 0 deletions tests/utils_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
from dateutil.parser import isoparse
import jsonschema
from typeguard import check_type # type: ignore
from typing import Sequence
import pytest

dir_path = os.path.dirname(os.path.realpath(__file__))
SAMPLE_JSON = path.join(dir_path, "resources", "sample.json")
Expand Down Expand Up @@ -149,6 +151,35 @@ def test_writing_json():
assert original_exchanges == exchanges


@pytest.fixture
def exchanges():
with open(SAMPLE_JSONL, "r", encoding="utf-8") as f:
return [exchange for exchange in HttpExchangeReader.from_jsonl(f)]


def test_serializing_to_dict(exchanges: Sequence[HttpExchange]):
exchange = exchanges[0]
as_dict = HttpExchangeWriter.to_dict(exchange)
assert "request" in as_dict
assert "response" in as_dict
request = as_dict["request"]
assert "method" in request
assert request["method"] == "get"
assert "headers" in request
assert "query" in request
response = as_dict["response"]
assert "body" in response
assert isinstance(response["body"], str)


def test_serializing_to_json_and_back(exchanges: Sequence[HttpExchange]):
exchange = exchanges[0]
as_json = HttpExchangeWriter.to_json(exchange)
assert isinstance(as_json, str)
decoded = HttpExchangeReader.from_json(as_json)
assert isinstance(decoded, HttpExchange)


def test_example_from_readme():
request = RequestBuilder.from_dict(
{
Expand Down

0 comments on commit 86e5c19

Please sign in to comment.