Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion docs/changelog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# Changelog

### v0.4.2
- FIX: Fixed serialization of extended fields (e.g., DevTools) in HarLog dump. Now all additional fields are correctly preserved when calling model_dump().

### v0.4.1
- FIX: Removed the mistakenly added `rich` dependency from the requirements.

Expand Down Expand Up @@ -44,4 +47,4 @@

## v0.1.0

- Initial release of `hario-core`.
- Initial release of `hario-core`.
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"

[project]
name = "hario-core"
version = "0.4.1"
version = "0.4.2"
description = "Modern, type-safe, and extensible library for parsing, transforming, and analyzing HAR (HTTP Archive) files."
authors = [{name = "Vasiliy Pikulev", email = "pikulev.vasiliy@gmail.com"}]
readme = "README.md"
Expand Down Expand Up @@ -106,7 +106,7 @@ convention = "google"

[tool.poetry]
name = "hario-core"
version = "0.4.1"
version = "0.4.2"
description = "Modern, type-safe, and extensible library for parsing, transforming, and analyzing HAR (HTTP Archive) files."
authors = ["Vasiliy Pikulev <pikulev.vasiliy@gmail.com>"]
readme = "README.md"
Expand Down
2 changes: 1 addition & 1 deletion src/hario_core/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
Hario Core package root.
"""

__version__ = "0.4.1"
__version__ = "0.4.2"

from . import models, parse, transform

Expand Down
7 changes: 6 additions & 1 deletion src/hario_core/models/extensions/chrome_devtools.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

from typing import List, Optional

from pydantic import BaseModel, Field
from pydantic import BaseModel, ConfigDict, Field

from ..har_1_2 import Entry, Request, Response, Timings

Expand Down Expand Up @@ -68,6 +68,11 @@ class DevToolsWebSocketMessage(BaseModel):
class DevToolsEntry(Entry):
"""HAR Entry object with DevTools extensions."""

model_config = ConfigDict(
extra="allow",
populate_by_name=True,
)

initiator: Optional[DevToolsInitiator] = Field(None, alias="_initiator")
priority: Optional[str] = Field(None, alias="_priority")
resourceType: str = Field(alias="_resourceType")
Expand Down
16 changes: 14 additions & 2 deletions src/hario_core/models/har_1_2.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,8 @@ class Entry(BaseModel):
"""HAR Entry object."""

model_config = ConfigDict(
extra="forbid",
extra="allow",
populate_by_name=True,
)

pageref: Optional[str] = None
Expand Down Expand Up @@ -135,10 +136,21 @@ class Page(BaseModel):


class HarLog(BaseModel):
model_config = ConfigDict(extra="forbid") # strict: forbid vendor‑specific fields
model_config = ConfigDict(
extra="allow",
populate_by_name=True,
)

version: str
creator: Creator
browser: Optional[Browser] = None
pages: List[Page] = []
entries: List[Entry]

def model_dump(self: HarLog, **kwargs: Any) -> Dict[str, Any]:
dump = super().model_dump(**kwargs)
# manually serialize entries with the actual type
dump["entries"] = [
entry.model_dump(**kwargs) for entry in self.entries if entry is not None
]
return dump
4 changes: 2 additions & 2 deletions tests/samples.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,8 +156,6 @@
}
}

CHROME_DEVTOOLS_HAR_BYTES: bytes = orjson.dumps(CHROME_DEVTOOLS_HAR)

# Valid HAR 1.2
CLEANED_HAR: Dict[str, Any] = {
"log": {
Expand Down Expand Up @@ -274,6 +272,8 @@

CLEANED_HAR_BYTES: bytes = orjson.dumps(CLEANED_HAR)

CHROME_DEVTOOLS_HAR_BYTES: bytes = orjson.dumps(CHROME_DEVTOOLS_HAR)

# Edge-case: HAR without log field (based on real HAR)
INVALID_HAR_NO_LOG: Dict[str, Any] = {
k: v for k, v in CHROME_DEVTOOLS_HAR.items() if k != "log"
Expand Down
19 changes: 19 additions & 0 deletions tests/test_har_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -240,3 +240,22 @@ def test_validate_with_invalid_entries(self, cleaned_har: Dict[str, Any]) -> Non
har["log"]["entries"] = dict()
with pytest.raises(ValueError):
validate(har)

def test_har_log_model_dump_preserves_extension_fields(self) -> None:
"""
Test that har_log.model_dump() preserves
Chrome DevTools extension fields at all levels.
"""
har_log = parse(CHROME_DEVTOOLS_HAR_BYTES)
dumped = har_log.model_dump()
# Check that entries exists and is not empty
assert "entries" in dumped
assert len(dumped["entries"]) > 0
entry_dump = dumped["entries"][0]
# Check that DevTools extension fields are preserved
assert "initiator" in entry_dump
assert "connectionId" in entry_dump
# Check that nested fields are preserved
assert isinstance(entry_dump["initiator"], dict)
assert entry_dump["initiator"]["type"] == "parser"
assert "transferSize" in entry_dump["response"]