Skip to content

Commit

Permalink
Merge pull request #48 from zalando-incubator/har-entry-fields
Browse files Browse the repository at this point in the history
Expose all HAR "entry" fields
  • Loading branch information
tortila committed Apr 25, 2019
2 parents b0fda74 + 828ed8a commit c23f291
Show file tree
Hide file tree
Showing 9 changed files with 88 additions and 9 deletions.
2 changes: 2 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ python:
- '3.6'
- '3.7'
install:
# workaround for backward compatibility to Poetry: https://github.com/sdispater/poetry/issues/1049
- pip install --upgrade pip==18.1
- pip install poetry codacy-coverage
- poetry install
script:
Expand Down
19 changes: 19 additions & 0 deletions docs/Changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,25 @@ The format is based on `Keep a Changelog`_, and this project adheres to
:local:
:depth: 1

.. _v1.1.2:

v1.1.2
======

- Release date: 2019-04-25 14:49

- Diff__.

__ https://github.com/zalando-incubator/transformer/compare/v1.1.1...v1.1.2

Added
-----

:attr:`transformer.request.Request.har_entry`
This new read-only property contains the entry as recorded in a HAR file,
corresponding to the specific :class:`Request <transformer.request.Request>` object.
As requested by :user:`xinke2411` (:issue:`35`)

.. _v1.1.1:

v1.1.1
Expand Down
8 changes: 8 additions & 0 deletions docs/Writing-plugins.rst
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,14 @@ that make requests to a specific URL.
It would not need to modify :term:`scenario` objects or the :term:`syntax tree`
directly.

.. warning::

Modifying :any:`har_entry <transformer.request.Request.har_entry>` property
of a :class:`Request <transformer.request.Request>` object will not have any effect on the resulting
:term:`task`. The field serves the purpose of exposing all data recorded in a HAR file corresponding
to the specific :class:`Request <transformer.request.Request>`, that might have otherwise not been reflected
in the intermediate representation.

To let Transformer know that this authentication plugin must be executed with
:term:`task` objects passed as input (and not, say, :term:`scenario` objects),
the plugin's author must announce that **the plugin satisfies a specific
Expand Down
2 changes: 1 addition & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
# The short X.Y version
version = "1.1"
# The full version, including alpha/beta/rc tags
release = "1.1.1"
release = "1.1.2"


# -- General configuration ---------------------------------------------------
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "har-transformer"
version = "1.1.1"
version = "1.1.2"
description = "A tool to convert HAR files into a locustfile."
authors = [
"Serhii Cherniavskyi <serhii.cherniavskyi@zalando.de>",
Expand Down
1 change: 1 addition & 0 deletions transformer/plugins/test_sanitize_headers.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ def task_with_header(name: str, value: str) -> Task2:
timestamp=TS,
method=HttpMethod.GET,
url=urlparse("https://example.com"),
har_entry={"entry": "data"},
name="task_name",
headers=[Header(name=name, value=value)],
post_data={},
Expand Down
21 changes: 15 additions & 6 deletions transformer/request.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,18 +67,25 @@ class Request:
.. attribute:: timestamp
:class:`~datetime.datetime` --
Time at which the request was recorded.
:class:`~datetime.datetime` --
Time at which the request was recorded.
.. attribute:: method
:class:`HttpMethod` --
HTTP method of the request.
:class:`HttpMethod` --
HTTP method of the request.
.. attribute:: url
:class:`urllib.parse.SplitResult` --
URL targeted by the request.
:class:`urllib.parse.SplitResult` --
URL targeted by the request.
.. attribute:: har_entry
:any:`dict` --
A single record from entries as recorded in a HAR file
(http://www.softwareishard.com/blog/har-12-spec/#entries)
corresponding to the request, provided for read-only access.
.. attribute:: headers
:annotation: = []
Expand Down Expand Up @@ -115,6 +122,7 @@ class Request:
timestamp: datetime
method: HttpMethod
url: SplitResult
har_entry: dict
headers: List[Header] = ()
post_data: Optional[dict] = None
query: List[QueryPair] = ()
Expand All @@ -141,6 +149,7 @@ def from_har_entry(cls, entry: dict) -> "Request":
timestamp=pendulum.parse(entry["startedDateTime"]),
method=HttpMethod[request["method"]],
url=urlparse(request["url"]),
har_entry=entry,
name=None,
headers=[
Header(name=d["name"], value=d["value"])
Expand Down
27 changes: 27 additions & 0 deletions transformer/test_request.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,33 @@ def test_it_returns_a_request_with_a_query_given_a_delete_request_with_a_query(
assert request.method == HttpMethod.DELETE
assert request.query == [QueryPair(name="some name", value="some value")]

def test_it_records_har_entry(self):
entry = {
"request": {
"method": "GET",
"url": "localhost"
},
"response": {
"status": 200,
"statusText": "OK",
},
"cache": {},
"timings": {
"connect": 22,
"wait": 46,
"receive": 0
},
"startedDateTime": "2018-01-01",
"time": 116,
"_securityState": "secure",
"connection": "443"
}
request = Request.from_har_entry(entry)
assert isinstance(request, Request)
assert request.har_entry
assert str(request.url.geturl()) == request.har_entry["request"]["url"]
assert request.har_entry["_securityState"] == "secure"


class TestAllFromHar:
@pytest.mark.skip(reason="Doesn't raise AssertionError; to be investigated.")
Expand Down
15 changes: 14 additions & 1 deletion transformer/test_task.py
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,7 @@ def test_it_supports_get_requests(self):
timestamp=MagicMock(),
method=HttpMethod.GET,
url=urlparse(url),
har_entry={"entry": "data"},
headers=[Header("a", "b")],
query=[QueryPair("x", "y")], # query is currently ignored for GET
)
Expand All @@ -239,6 +240,7 @@ def test_it_supports_urlencoded_post_requests(self):
timestamp=MagicMock(),
method=HttpMethod.POST,
url=urlparse(url),
har_entry={"entry": "data"},
headers=[Header("a", "b")],
post_data={
"mimeType": "application/x-www-form-urlencoded",
Expand All @@ -265,6 +267,7 @@ def test_it_supports_json_post_requests(self):
timestamp=MagicMock(),
method=HttpMethod.POST,
url=urlparse(url),
har_entry={"entry": "data"},
headers=[Header("a", "b")],
post_data={
"mimeType": "application/json",
Expand All @@ -291,6 +294,7 @@ def test_it_supports_empty_post_requests(self):
timestamp=MagicMock(),
method=HttpMethod.POST,
url=urlparse(url),
har_entry={"entry": "data"},
headers=[Header("a", "b")],
post_data=None,
)
Expand All @@ -311,6 +315,7 @@ def test_it_supports_put_requests_with_payload(self):
timestamp=MagicMock(),
method=HttpMethod.PUT,
url=urlparse(url),
har_entry={"entry": "data"},
headers=[Header("a", "b")],
query=[QueryPair("c", "d")],
post_data={
Expand Down Expand Up @@ -338,6 +343,7 @@ def test_it_supports_put_requests_without_payload(self):
timestamp=MagicMock(),
method=HttpMethod.PUT,
url=urlparse(url),
har_entry={"entry": "data"},
headers=[Header("a", "b")],
query=[QueryPair("c", "d")],
post_data=None,
Expand All @@ -358,7 +364,7 @@ def test_it_uses_the_custom_name_if_provided(self):
url = "http://abc.de"
name = "my-req"
r = Request(
name=name, timestamp=MagicMock(), method=HttpMethod.GET, url=urlparse(url)
name=name, timestamp=MagicMock(), method=HttpMethod.GET, url=urlparse(url), har_entry={"entry": "data"}
)
assert req_to_expr(r) == py.FunctionCall(
name="self.client.get",
Expand All @@ -379,6 +385,7 @@ def test_it_supports_get_requests(self):
timestamp=MagicMock(),
method=HttpMethod.GET,
url=urlparse(url),
har_entry={"entry": "data"},
headers=[Header("a", "b")],
query=[QueryPair("x", "y")], # query is currently ignored for GET
)
Expand Down Expand Up @@ -415,6 +422,7 @@ def test_it_supports_urlencoded_post_requests(self):
timestamp=MagicMock(),
method=HttpMethod.POST,
url=urlparse(url),
har_entry={"entry": "data"},
headers=[Header("a", "b")],
post_data={
"mimeType": "application/x-www-form-urlencoded",
Expand Down Expand Up @@ -443,6 +451,7 @@ def test_it_supports_json_post_requests(self):
timestamp=MagicMock(),
method=HttpMethod.POST,
url=urlparse(url),
har_entry={"entry": "data"},
headers=[Header("a", "b")],
post_data={
"mimeType": "application/json",
Expand Down Expand Up @@ -471,6 +480,7 @@ def test_it_supports_empty_post_requests(self):
timestamp=MagicMock(),
method=HttpMethod.POST,
url=urlparse(url),
har_entry={"entry": "data"},
headers=[Header("a", "b")],
post_data=None,
)
Expand All @@ -493,6 +503,7 @@ def test_it_supports_put_requests_with_payload(self):
timestamp=MagicMock(),
method=HttpMethod.PUT,
url=urlparse(url),
har_entry={"entry": "data"},
headers=[Header("a", "b")],
query=[QueryPair("c", "d")],
post_data={
Expand Down Expand Up @@ -522,6 +533,7 @@ def test_it_supports_put_requests_without_payload(self):
timestamp=MagicMock(),
method=HttpMethod.PUT,
url=urlparse(url),
har_entry={"entry": "data"},
headers=[Header("a", "b")],
query=[QueryPair("c", "d")],
post_data=None,
Expand All @@ -548,6 +560,7 @@ def test_it_uses_the_custom_name_if_provided(self):
timestamp=MagicMock(),
method=HttpMethod.GET,
url=urlparse(url),
har_entry={"entry": "data"}
)
)
assert lreq_to_expr(r) == py.FunctionCall(
Expand Down

0 comments on commit c23f291

Please sign in to comment.