# Copyright (c) 2026 Patrick Motsch # All rights reserved. """Workflow action: update a single Redmine ticket and refresh the mirror.""" import logging from typing import Any, Dict from modules.datamodels.datamodelChat import ActionResult from modules.features.redmine.datamodelRedmine import RedmineTicketUpdateRequest from modules.features.redmine.serviceRedmine import updateTicket from ._shared import resolveInstanceContext, ticketToDict logger = logging.getLogger(__name__) async def updateTicketAction(self, parameters: Dict[str, Any]) -> ActionResult: """Update ``parameters['ticketId']`` with the given fields. Only fields that are not ``None`` (and different from the current Redmine state, enforced by the service) are sent to Redmine. An optional ``notes`` string is appended as a journal entry. """ try: user, mandateId, featureInstanceId = resolveInstanceContext(self.services, parameters) except ValueError as exc: return ActionResult.isFailure(error=str(exc)) raw_id = parameters.get("ticketId") or parameters.get("issueId") if raw_id is None: return ActionResult.isFailure(error="ticketId is required") try: ticketId = int(raw_id) except (TypeError, ValueError): return ActionResult.isFailure(error=f"ticketId must be an int, got {raw_id!r}") try: update = RedmineTicketUpdateRequest( subject=parameters.get("subject"), description=parameters.get("description"), trackerId=_optInt(parameters.get("trackerId")), statusId=_optInt(parameters.get("statusId")), priorityId=_optInt(parameters.get("priorityId")), assignedToId=_optInt(parameters.get("assignedToId")), parentIssueId=_optInt(parameters.get("parentIssueId")), fixedVersionId=_optInt(parameters.get("fixedVersionId")), notes=parameters.get("notes"), customFields=parameters.get("customFields") or None, ) except Exception as exc: return ActionResult.isFailure(error=f"Invalid update body: {exc}") try: updated = await updateTicket(user, mandateId, featureInstanceId, ticketId, update) except Exception as exc: logger.exception("redmine.updateTicket failed for ticket %s", ticketId) return ActionResult.isFailure(error=f"Update ticket failed: {exc}") return ActionResult.isSuccess(data={"ticket": ticketToDict(updated)}) def _optInt(value: Any): if value is None or value == "": return None try: return int(value) except (TypeError, ValueError): return None