Skip to content

Commit 212f9db

Browse files
authored
fix(jira): serialize datetime in changelog for JSON compatibility (#768)
Add field_serializer to JiraChangelog model to ensure datetime fields are properly serialized to ISO 8601 format for JSON. This fixes the "Object of type datetime is not JSON serializable" error when using expand='changelog' with Jira search. - Add @field_serializer("created") to JiraChangelog - Update to_simplified_dict() to use isoformat() - Add regression tests for datetime serialization - Update existing test expectations for ISO 8601 format Fixes #749 Github-Issue:#749
1 parent 6e883ad commit 212f9db

File tree

3 files changed

+58
-6
lines changed

3 files changed

+58
-6
lines changed

src/mcp_atlassian/models/jira/common.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from datetime import datetime
1010
from typing import Any
1111

12-
from pydantic import Field
12+
from pydantic import Field, field_serializer
1313

1414
from mcp_atlassian.utils import parse_date
1515

@@ -518,6 +518,13 @@ class JiraChangelog(ApiModel, TimestampMixin):
518518
created: datetime | None = None
519519
items: list[JiraChangelogItem] = Field(default_factory=list)
520520

521+
@field_serializer("created")
522+
def serialize_created(self, value: datetime | None) -> str | None:
523+
"""Serialize datetime to ISO 8601 string for JSON compatibility."""
524+
if value is None:
525+
return None
526+
return value.isoformat()
527+
521528
@classmethod
522529
def from_api_response(cls, data: dict[str, Any], **kwargs: Any) -> "JiraChangelog":
523530
"""
@@ -578,6 +585,6 @@ def to_simplified_dict(self) -> dict[str, Any]:
578585
result["author"] = self.author.to_simplified_dict()
579586

580587
if self.created:
581-
result["created"] = str(self.created)
588+
result["created"] = self.created.isoformat()
582589

583590
return result

tests/unit/jira/test_issues.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1357,7 +1357,7 @@ def test_batch_get_changelogs_cloud(self, issues_mixin: IssuesMixin):
13571357
"email": None,
13581358
"name": "Test User 1",
13591359
},
1360-
"created": "2024-01-05 10:06:03.548000+08:00",
1360+
"created": "2024-01-05T10:06:03.548000+08:00",
13611361
"items": [
13621362
{
13631363
"field": "IssueParentAssociation",
@@ -1382,7 +1382,7 @@ def test_batch_get_changelogs_cloud(self, issues_mixin: IssuesMixin):
13821382
"email": None,
13831383
"name": "Test User 2",
13841384
},
1385-
"created": "2024-01-01 11:00:00+00:00",
1385+
"created": "2024-01-01T11:00:00+00:00",
13861386
"items": [
13871387
{
13881388
"field": "Parent",
@@ -1399,7 +1399,7 @@ def test_batch_get_changelogs_cloud(self, issues_mixin: IssuesMixin):
13991399
"email": None,
14001400
"name": "Test User 3",
14011401
},
1402-
"created": "2024-01-06 10:06:03.548000+08:00",
1402+
"created": "2024-01-06T10:06:03.548000+08:00",
14031403
"items": [
14041404
{
14051405
"field": "Parent",
@@ -1418,7 +1418,7 @@ def test_batch_get_changelogs_cloud(self, issues_mixin: IssuesMixin):
14181418
"email": None,
14191419
"name": "Test User 1",
14201420
},
1421-
"created": "2024-01-10 10:06:03.548000+08:00",
1421+
"created": "2024-01-10T10:06:03.548000+08:00",
14221422
"items": [
14231423
{
14241424
"field": "Parent",

tests/unit/models/test_jira_models.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@
55
and the simplified dictionary conversion for API responses.
66
"""
77

8+
import json
89
import os
910
import re
11+
from datetime import datetime, timezone
1012

1113
import pytest
1214

@@ -35,6 +37,7 @@
3537
JiraUser,
3638
JiraWorklog,
3739
)
40+
from src.mcp_atlassian.models.jira.common import JiraChangelog
3841

3942
# Optional: Import real API client for optional real-data testing
4043
try:
@@ -1945,3 +1948,45 @@ def test_real_jira_worklog(self, use_real_jira_data, default_jira_issue_key):
19451948

19461949
except Exception as e:
19471950
pytest.fail(f"Error testing real Jira worklog: {e}")
1951+
1952+
1953+
class TestJiraChangelog:
1954+
"""Tests for JiraChangelog datetime serialization (fixes #749)."""
1955+
1956+
def test_created_datetime_serialization(self):
1957+
"""Test that datetime created field serializes to JSON properly."""
1958+
changelog = JiraChangelog(
1959+
id="12345",
1960+
created=datetime(2024, 1, 15, 10, 30, 0, tzinfo=timezone.utc),
1961+
items=[],
1962+
)
1963+
1964+
# model_dump should serialize datetime to ISO string
1965+
dumped = changelog.model_dump(mode="json")
1966+
assert isinstance(dumped["created"], str)
1967+
assert "2024-01-15" in dumped["created"]
1968+
1969+
# to_simplified_dict should also work
1970+
simplified = changelog.to_simplified_dict()
1971+
assert isinstance(simplified["created"], str)
1972+
1973+
# Final json.dumps should not raise
1974+
json_str = json.dumps(simplified)
1975+
assert "2024-01-15" in json_str
1976+
1977+
def test_from_api_response_with_changelog(self):
1978+
"""Test JiraChangelog.from_api_response handles dates correctly."""
1979+
data = {
1980+
"id": "100",
1981+
"created": "2024-01-15T10:30:00.000+0000",
1982+
"author": {"displayName": "Test User"},
1983+
"items": [{"field": "status", "fromString": "Open", "toString": "Done"}],
1984+
}
1985+
1986+
changelog = JiraChangelog.from_api_response(data)
1987+
assert isinstance(changelog.created, datetime)
1988+
1989+
# Should serialize cleanly to JSON
1990+
simplified = changelog.to_simplified_dict()
1991+
json_str = json.dumps(simplified)
1992+
assert "2024-01-15" in json_str

0 commit comments

Comments
 (0)