Skip to content

Commit b8c1f1b

Browse files
franccescoclaude
andcommitted
fix(operations): make update() methods return updated objects consistently
headline.update() and goal.update() returned None while todo.update() and issue.update() returned the updated entity. Now all update() methods re-fetch and return the updated object after the PUT call. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent df740f4 commit b8c1f1b

5 files changed

Lines changed: 94 additions & 4 deletions

File tree

src/bloomy/operations/async_/goals.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,13 +91,31 @@ async def delete(self, goal_id: int) -> None:
9191
response = await self._client.delete(f"rocks/{goal_id}")
9292
response.raise_for_status()
9393

94+
async def details(self, goal_id: int) -> GoalInfo:
95+
"""Get details for a specific goal.
96+
97+
Args:
98+
goal_id: The ID of the goal
99+
100+
Returns:
101+
A GoalInfo model instance containing the goal details
102+
103+
"""
104+
response = await self._client.get(
105+
f"rocks/{goal_id}", params={"include_origin": True}
106+
)
107+
response.raise_for_status()
108+
data = response.json()
109+
110+
return self._transform_goal_details(data)
111+
94112
async def update(
95113
self,
96114
goal_id: int,
97115
title: str | None = None,
98116
accountable_user: int | None = None,
99117
status: GoalStatus | str | None = None,
100-
) -> None:
118+
) -> GoalInfo:
101119
"""Update a goal.
102120
103121
Args:
@@ -110,12 +128,17 @@ async def update(
110128
GoalStatus.AT_RISK, or GoalStatus.COMPLETE for type safety.
111129
Invalid values will raise ValueError via the update payload builder.
112130
131+
Returns:
132+
A GoalInfo model instance containing the updated goal details
133+
113134
"""
114135
payload = self._build_goal_update_payload(accountable_user, title, status)
115136

116137
response = await self._client.put(f"rocks/{goal_id}", json=payload)
117138
response.raise_for_status()
118139

140+
return await self.details(goal_id)
141+
119142
async def archive(self, goal_id: int) -> None:
120143
"""Archive a rock with the specified goal ID.
121144

src/bloomy/operations/async_/headlines.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,18 +59,23 @@ async def create(
5959
notes_url=data.get("DetailsUrl") or "",
6060
)
6161

62-
async def update(self, headline_id: int, title: str) -> None:
62+
async def update(self, headline_id: int, title: str) -> HeadlineDetails:
6363
"""Update a headline.
6464
6565
Args:
6666
headline_id: The ID of the headline to update
6767
title: The new title of the headline
6868
69+
Returns:
70+
A HeadlineDetails model instance containing the updated headline
71+
6972
"""
7073
payload = {"title": title}
7174
response = await self._client.put(f"headline/{headline_id}", json=payload)
7275
response.raise_for_status()
7376

77+
return await self.details(headline_id)
78+
7479
async def details(self, headline_id: int) -> HeadlineDetails:
7580
"""Get headline details.
7681

src/bloomy/operations/goals.py

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,13 +124,35 @@ def delete(self, goal_id: int) -> None:
124124
response = self._client.delete(f"rocks/{goal_id}")
125125
response.raise_for_status()
126126

127+
def details(self, goal_id: int) -> GoalInfo:
128+
"""Get details for a specific goal.
129+
130+
Args:
131+
goal_id: The ID of the goal
132+
133+
Returns:
134+
A GoalInfo model instance containing the goal details
135+
136+
Example:
137+
```python
138+
client.goal.details(1)
139+
# Returns: GoalInfo(id=1, title='Complete project', ...)
140+
```
141+
142+
"""
143+
response = self._client.get(f"rocks/{goal_id}", params={"include_origin": True})
144+
response.raise_for_status()
145+
data = response.json()
146+
147+
return self._transform_goal_details(data)
148+
127149
def update(
128150
self,
129151
goal_id: int,
130152
title: str | None = None,
131153
accountable_user: int | None = None,
132154
status: GoalStatus | str | None = None,
133-
) -> None:
155+
) -> GoalInfo:
134156
"""Update a goal.
135157
136158
Args:
@@ -143,6 +165,9 @@ def update(
143165
GoalStatus.AT_RISK, or GoalStatus.COMPLETE for type safety.
144166
Invalid values will raise ValueError via the update payload builder.
145167
168+
Returns:
169+
A GoalInfo model instance containing the updated goal details
170+
146171
Example:
147172
```python
148173
from bloomy import GoalStatus
@@ -160,6 +185,8 @@ def update(
160185
response = self._client.put(f"rocks/{goal_id}", json=payload)
161186
response.raise_for_status()
162187

188+
return self.details(goal_id)
189+
163190
def archive(self, goal_id: int) -> None:
164191
"""Archive a rock with the specified goal ID.
165192

src/bloomy/operations/headlines.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,18 +55,23 @@ def create(
5555
notes_url=data.get("DetailsUrl") or "",
5656
)
5757

58-
def update(self, headline_id: int, title: str) -> None:
58+
def update(self, headline_id: int, title: str) -> HeadlineDetails:
5959
"""Update a headline.
6060
6161
Args:
6262
headline_id: The ID of the headline to update
6363
title: The new title of the headline
6464
65+
Returns:
66+
A HeadlineDetails model instance containing the updated headline
67+
6568
"""
6669
payload = {"title": title}
6770
response = self._client.put(f"headline/{headline_id}", json=payload)
6871
response.raise_for_status()
6972

73+
return self.details(headline_id)
74+
7075
def details(self, headline_id: int) -> HeadlineDetails:
7176
"""Get headline details.
7277

src/bloomy/operations/mixins/goals_transform.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,36 @@ def _transform_goal_list(self, data: Sequence[dict[str, Any]]) -> list[GoalInfo]
5151
for goal in data
5252
]
5353

54+
def _transform_goal_details(self, data: dict[str, Any]) -> GoalInfo:
55+
"""Transform a single goal API response to a GoalInfo model.
56+
57+
Args:
58+
data: The raw API response data for a single goal.
59+
60+
Returns:
61+
A GoalInfo model.
62+
63+
"""
64+
return GoalInfo(
65+
id=data["Id"],
66+
user_id=data["Owner"]["Id"],
67+
user_name=data["Owner"]["Name"],
68+
title=data["Name"],
69+
created_at=data["CreateTime"],
70+
due_date=data["DueDate"],
71+
status="Completed" if data.get("Complete") else "Incomplete",
72+
meeting_id=(
73+
data["Origins"][0]["Id"]
74+
if data.get("Origins") and data["Origins"][0]
75+
else None
76+
),
77+
meeting_title=(
78+
data["Origins"][0]["Name"]
79+
if data.get("Origins") and data["Origins"][0]
80+
else None
81+
),
82+
)
83+
5484
def _transform_archived_goals(
5585
self, data: Sequence[dict[str, Any]]
5686
) -> list[ArchivedGoalInfo]:

0 commit comments

Comments
 (0)