fix: make Search watch admission recoverable (Closes #1295)
This commit is contained in:
1 parent
4fe07bcd52
commit
6c12731215
7 files changed
+213
-15
No files matched your search
@@ -113,6 +113,27 @@ class FollowingStore:
|
||||
)
|
||||
return snapshot
|
||||
|
||||
def preflight(self, login: str, raw_item: dict, watching: bool) -> dict:
|
||||
"""Validate a requested change and capacity without mutating the registry."""
|
||||
login = self._login(login)
|
||||
item = self._normalize_item(raw_item)
|
||||
if not isinstance(watching, bool):
|
||||
raise ValueError("watching is invalid")
|
||||
if not watching:
|
||||
return item
|
||||
identity = self._identity(item)
|
||||
with self._connect() as connection:
|
||||
row = connection.execute(
|
||||
"SELECT revision, items FROM following_issues WHERE login = ?", (login,)
|
||||
).fetchone()
|
||||
current, _legacy = self._snapshot(row, login)
|
||||
if (
|
||||
len(current["items"]) >= self.limit
|
||||
and not any(self._identity(candidate) == identity for candidate in current["items"])
|
||||
):
|
||||
raise ValueError(f"following is limited to {self.limit} issues")
|
||||
return item
|
||||
|
||||
def set_watching(self, login: str, raw_item: dict, watching: bool) -> dict:
|
||||
login = self._login(login)
|
||||
item = self._normalize_item(raw_item)
|
||||
|
||||
@@ -608,6 +608,8 @@ async def work_preview(repository: str, kind: str, number: int) -> dict:
|
||||
"title": issue.get("title", "") if isinstance(issue.get("title"), str) else "",
|
||||
"body": issue.get("body", "") if isinstance(issue.get("body"), str) else "",
|
||||
"state": state,
|
||||
"updated_at": issue.get("updated_at", "")
|
||||
if isinstance(issue.get("updated_at"), str) else "",
|
||||
"author": author.get("login", "") if isinstance(author.get("login"), str) else "",
|
||||
"labels": [
|
||||
label["name"] for label in labels
|
||||
|
||||
+31
-14
@@ -3738,26 +3738,43 @@ async def mutate_global_search_preview_subscription(
|
||||
repository, preview = await _search_preview_subscription_target(owner, repo, number, kind)
|
||||
login = await _confirmed_login()
|
||||
store = _following_store()
|
||||
await asyncio.to_thread(store.get, login)
|
||||
following_item = {
|
||||
"repository": repository,
|
||||
"number": number,
|
||||
"title": preview.get("title", ""),
|
||||
"state": preview.get("state", ""),
|
||||
"updated_at": preview.get("updated_at", ""),
|
||||
"url": preview.get("url", ""),
|
||||
}
|
||||
await asyncio.to_thread(store.preflight, login, following_item, watching)
|
||||
result = await asyncio.wait_for(
|
||||
gitea_proxy.set_issue_subscription(repository, number, watching),
|
||||
timeout=ISSUE_ACTION_TIMEOUT_SECONDS,
|
||||
)
|
||||
if result.get("watching") is not watching:
|
||||
raise RuntimeError("Gitea did not confirm subscription state")
|
||||
following = await asyncio.to_thread(
|
||||
store.set_watching,
|
||||
login,
|
||||
{
|
||||
"repository": repository,
|
||||
"number": number,
|
||||
"title": preview.get("title", ""),
|
||||
"state": preview.get("state", ""),
|
||||
"updated_at": preview.get("updated_at", ""),
|
||||
"url": preview.get("url", ""),
|
||||
},
|
||||
watching,
|
||||
)
|
||||
try:
|
||||
following = await asyncio.to_thread(
|
||||
store.set_watching,
|
||||
login,
|
||||
following_item,
|
||||
watching,
|
||||
)
|
||||
except Exception:
|
||||
try:
|
||||
compensated = await asyncio.wait_for(
|
||||
gitea_proxy.set_issue_subscription(repository, number, not watching),
|
||||
timeout=ISSUE_ACTION_TIMEOUT_SECONDS,
|
||||
)
|
||||
except Exception:
|
||||
compensated = None
|
||||
if not isinstance(compensated, dict) or compensated.get("watching") is not (not watching):
|
||||
return JSONResponse({
|
||||
**result,
|
||||
"following_synced": False,
|
||||
"error": "Watching in Gitea, but Following could not sync. Retry this action.",
|
||||
})
|
||||
raise
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception:
|
||||
|
||||
Reference in new issue
Block a user