指定工单可加评论,JQL拉取工单调用Jira REST API v3报404
问题:Jira API批量添加评论时404错误
手动指定工单编号调用Jira REST API v3添加评论正常,但通过pull_ticket_info函数(基于JQL查询拉取工单信息)返回的工单编号调用add_comment时,抛出HTTP 404错误,提示:
问题不存在或您没有查看权限。
已确认信息:
- 两次调用使用相同认证凭据,且权限验证通过
- 打印工单编号显示为正确的Issue Key格式
- 尝试过去除多余空格、改用Issue ID,问题未解决
相关代码如下:
pull_ticket_info函数
def pull_ticket_info(jql_query, jira_url, api_token, jira_username): """ Retrieve ticket information from Jira using a JQL query. Args: jql_query (str): The JQL query to search for tickets in Jira. jira_url (str): The URL of the Jira instance. api_token (str): The API token for authentication with Jira. jira_username (str): The username for authentication with Jira. Returns: str: The ticket number of research tickets. Returns None if no ticket is found. """ ticket_info_dict = {} # Dictionary to store ticket number and research type # Set up the API endpoint for JQL search api_endpoint = f"{jira_url}/rest/api/3/search" # Encode credentials for authentication credentials = f"{jira_username}:{api_token}" base64_credentials = base64.b64encode(credentials.encode()).decode() # Set up the authentication headers with base64-encoded credentials headers = { "Authorization": f"Basic {base64_credentials}" } # Set up the JQL query parameters and specify the fields you want to retrieve params = { "jql": jql_query, # "fields": "key, summary, customfield_13810, customfield_13427" } try: # Make the GET request to perform the JQL search jql_response = requests.get(api_endpoint, headers=headers, params=params) api_response = jql_response.json() # print(api_response) if jql_response.status_code == 200: for issue in api_response["issues"]: ticket_id = issue["id"] ticket_number = issue["key"] research_type = issue["fields"].get("customfield_13427", {}) research_type_value = research_type.get("value", "N/A") # Add ticket number and research type to the dictionary # ticket_info_dict[ticket_number] = research_type_value ticket_info_dict[ticket_number] = research_type_value # Return the dictionary with ticket numbers and research types as key-value pairs return ticket_info_dict else: # Handle errors if the JQL search request is unsuccessful print(f"JQL Error: {jql_response.status_code}, {api_response.get('errorMessages', 'Unknown error')}") return {} except requests.exceptions.RequestException as e: # Handle exceptions related to the HTTP request print(f"Request Error: {e}") return {}
add_comment函数
def add_comment(ticket_info_dict, jira_url, api_token, jira_username): """ Add a comment to a Jira ticket with documentation links based on the research type. Args: ticket_info_dict (dict): A dictionary containing ticket numbers as keys and research types as values. jira_url (str): The URL of the Jira instance. api_token (str): The API token for authentication with Jira. jira_username (str): The username for authentication with Jira. Returns: bool: True if all comments were added successfully, False otherwise. """ # Iterate through the ticket_info_dict to process each ticket and its research type for ticket_number, research_type in ticket_info_dict.items(): # Set up the API endpoint for adding comments to the ticket print(ticket_number) add_comment_endpoint = f"{jira_url}/rest/api/3/issue/{ticket_number}/comment" # Encode credentials for authentication credentials = f"{jira_username}:{api_token}" base64_credentials = base64.b64encode(credentials.encode()).decode() headers = { "Authorization": f"Basic {base64_credentials}", "Content-Type": "application/json", } # Check if the research type exists in the documentation dictionary if research_type in documentation_dict: # If the research type is found, include the corresponding documentation links in the comment documentation_links = documentation_dict[research_type] # Create the comment body including the comment_text and documentation links comment_body = { "body": { "content": [ { "type": "paragraph", "content": [ {"text": "This documentation relates to this research type:\n", "type": "text"}, ], }, { "type": "paragraph", "content": [{"text": "Documentation Links:\n", "type": "text"}], }, { "type": "paragraph", "content": [ { "text": f"-{doc_text}\n", "type": "text", "marks": [{"type": "link", "attrs": {"href": doc_link}}], } for doc_text, doc_link in documentation_links.items() ], }, ], "type": "doc", "version": 1 }, "properties": [ { "key": "sd.public.comment", "value": { "internal": "true" } } ], "visibility": { "type": "role", "value": "Service Desk Team" } } try: # Make the POST request to add the comment comment_response = requests.post(add_comment_endpoint, headers=headers, json=comment_body) # Check if the request was successful (HTTP status code 201) if comment_response.status_code == 201: print(f"Comment added successfully to ticket {ticket_number}.") else: print(f"Failed to add comment to ticket {ticket_number}. Status code: {comment_response.status_code}, Response: {comment_response.text}") except requests.exceptions.RequestException as e: # Handle exceptions related to the HTTP request print(f"Error: {e}") return False else: # If the research type is not found in the documentation dictionary, add a generic comment generic_comment = f"This ticket has a research type of '{research_type}' but no documentation links are available for this type." print(generic_comment) # Return True if all comments were added successfully return True
排查与修复方案
1. 验证Jira URL一致性
检查两个函数传入的jira_url是否完全一致,重点关注末尾是否带/:
- 若
jira_url为https://xxx.atlassian.net,拼接后接口为https://xxx.atlassian.net/rest/api/3/issue/GCPS-9679/comment - 若末尾多了
/,会变成https://xxx.atlassian.net//rest/api/3/issue/GCPS-9679/comment,直接导致404
可以在两个函数中打印最终API端点对比:
# 在pull_ticket_info中添加 print(f"Search endpoint: {api_endpoint}") # 在add_comment中添加 print(f"Comment endpoint: {add_comment_endpoint}")
2. 清理工单编号的不可见字符
即使打印的工单编号看起来正常,也可能存在全角空格、换行符等不可见字符。在add_comment中添加清理逻辑:
# 清理工单编号 cleaned_ticket_number = ticket_number.strip().replace('\n', '').replace('\r', '') # 打印字符编码对比差异 print(f"Original chars: {[ord(c) for c in ticket_number]}") print(f"Cleaned chars: {[ord(c) for c in cleaned_ticket_number]}") # 使用清理后的编号拼接接口 add_comment_endpoint = f"{jira_url}/rest/api/3/issue/{cleaned_ticket_number}/comment"
3. 检查评论可见性配置合法性
add_comment中设置的可见性角色Service Desk Team可能不存在,或当前用户无权限使用该角色限制评论可见性。临时注释以下代码测试:
# "properties": [ # { # "key": "sd.public.comment", # "value": { # "internal": "true" # } # } # ], # "visibility": { # "type": "role", # "value": "Service Desk Team" # }
4. 改用Jira官方SDK
原生requests易忽略API细节,改用jira库可简化会话管理和URL处理:
# 安装依赖 pip install jira # 重构示例代码 from jira import JIRA def pull_ticket_info(jql_query, jira_url, api_token, jira_username): jira = JIRA(server=jira_url, basic_auth=(jira_username, api_token)) issues = jira.search_issues(jql_query) ticket_info_dict = {} for issue in issues: research_type_field = issue.fields.customfield_13427 research_type_value = research_type_field.value if research_type_field else "N/A" ticket_info_dict[issue.key] = research_type_value return ticket_info_dict def add_comment(ticket_info_dict, jira_url, api_token, jira_username): jira = JIRA(server=jira_url, basic_auth=(jira_username, api_token)) for ticket_number, research_type in ticket_info_dict.items(): try: issue = jira.issue(ticket_number) if research_type in documentation_dict: comment_text = "This documentation relates to this research type:\n\nDocumentation Links:\n" for doc_text, doc_link in documentation_dict[research_type].items(): comment_text += f"- [{doc_text}]({doc_link})\n" jira.add_comment(issue, comment_text, is_internal=True, visibility={"type": "role", "value": "Service Desk Team"}) print(f"Comment added successfully to ticket {ticket_number}.") else: print(f"This ticket has a research type of '{research_type}' but no documentation links are available for this type.") except Exception as e: print(f"Failed to add comment to ticket {ticket_number}: {str(e)}") return True
内容的提问来源于stack exchange,提问作者Stephen Byrd
相关产品推荐
相关产品推荐

