diff --git a/jbi/jira/client.py b/jbi/jira/client.py index 9c1155fe..9ca88faa 100644 --- a/jbi/jira/client.py +++ b/jbi/jira/client.py @@ -78,6 +78,23 @@ def raise_for_status(self, *args, **kwargs): create_issue = instrumented_method(Jira.create_issue) get_project = instrumented_method(Jira.get_project) + @instrumented_method + def get_issue_transitions_with_fields(self, issue_key: str) -> list[dict]: + """Get available transitions for an issue with field metadata. + + This uses expand=transitions.fields to get information about which + fields are available on each transition screen. + + Args: + issue_key: The issue key (e.g., "SYNC-5055") + + Returns: + List of transition objects with field metadata + """ + url = f"rest/api/2/issue/{issue_key}/transitions" + response = self.get(url, params={"expand": "transitions.fields"}) + return response.get("transitions", []) if response else [] + @instrumented_method def paginated_projects( self, diff --git a/jbi/jira/service.py b/jbi/jira/service.py index 1bf3a55d..0602db5b 100644 --- a/jbi/jira/service.py +++ b/jbi/jira/service.py @@ -307,9 +307,24 @@ def update_issue_status(self, context: ActionContext, jira_status: str): kwargs: dict[str, Any] = {} if jira_status == "Cancelled": - kwargs["fields"] = { - "resolution": {"name": "Invalid"}, - } + # Check if resolution field is available on the transition screen + transitions = self.client.get_issue_transitions_with_fields(issue_key) + target_transition = next( + (t for t in transitions if t.get("to", {}).get("name") == jira_status), + None, + ) + + if target_transition and "resolution" in target_transition.get("fields", {}): + kwargs["fields"] = { + "resolution": {"name": "Invalid"}, + } + else: + logger.info( + "Resolution field not available on transition screen for %s, skipping", + issue_key, + extra=context.model_dump(), + ) + kwargs["update"] = { "comment": [{"add": {"body": "Issue was cancelled."}}], } diff --git a/tests/unit/jira/test_service.py b/tests/unit/jira/test_service.py index 19ede6fe..9dfd8f57 100644 --- a/tests/unit/jira/test_service.py +++ b/tests/unit/jira/test_service.py @@ -180,6 +180,25 @@ def test_update_issue_status_adds_comment_and_resolution_when_cancelled( context = action_context_factory(jira__issue="JBI-234") url = f"{settings.jira_base_url}rest/api/2/issue/JBI-234/transitions" + # Mock GET transitions with expand=transitions.fields (for checking resolution availability) + mocked_responses.add( + responses.GET, + url, + match=[ + responses.matchers.query_param_matcher({"expand": "transitions.fields"}) + ], + json={ + "transitions": [ + { + "name": "foo", + "id": 42, + "to": {"name": "Cancelled"}, + "fields": {"resolution": {"required": True}}, + } + ] + }, + ) + # Mock GET transitions (for set_issue_status to find transition ID) mocked_responses.add( responses.GET, url,