From 8c4e39e5a68d31597028a1af4d783280b684b07b Mon Sep 17 00:00:00 2001 From: jerubball Date: Tue, 6 Jan 2026 11:58:04 -0500 Subject: [PATCH 1/7] Add get_custom_field_options method --- atlassian/jira.py | 54 +++++++++++++++++++++++++++++++++++++++++++++++ docs/jira.rst | 15 ++++++++++--- 2 files changed, 66 insertions(+), 3 deletions(-) diff --git a/atlassian/jira.py b/atlassian/jira.py index 963427d5c..4273ecabb 100644 --- a/atlassian/jira.py +++ b/atlassian/jira.py @@ -713,6 +713,60 @@ def get_custom_field_option(self, option_id: T_id) -> T_resp_json: url = f"{base_url}/{option_id}" return self.get(url) + def get_custom_field_options( + self, + field_id: T_id, + project_id: T_id, + issue_type_id: Optional[T_id | list[T_id]] = None, + query: Optional[str] = None, + page: Optional[int] = None, + limit: Optional[int] = None, + sort: Optional[bool] = None, + use_all_contexts: Optional[bool] = None, + ) -> T_resp_json: + """ + Get list of all options available for a custom field in a specified project. + Numeric field ID and numeric project ID must be used. + + This is Experimental API available to Jira data Center. + At the time of testing, providing multiple project IDs results in 404 response. + + Reference: https://developer.atlassian.com/server/jira/platform/rest/v11003/api-group-customfields/#api-api-2-customfields-customfieldid-options-get + + :param field_id: str - The ID of the custom field. + :param project_id: str - The project ID in a context. + :param issue_type_id: str, Optional - A list of issue type IDs in a context. + :param query: str, Optional - A string used to filter options. + :param page: int, Optional - The page of options to return, starting from 1. + :param limit: int, Optional - The maximum number of results to return. If empty, return all results. + :param sort: bool, Optional - Flag to sort options by their names. + :param use_all_contexts: bool, Optional - Flag to fetch all options regardless of context, project IDs, or issue type IDs. + """ + url = self.resource_url( + f"customFields/{field_id}/options", + api_version=2, + ) + params: dict = {} + if project_id: + if isinstance(project_id, (list, tuple, set)): + project_id = ",".join(project_id) + params["projectIds"] = project_id + if issue_type_id: + if isinstance(issue_type_id, (list, tuple, set)): + issue_type_id = ",".join(issue_type_id) + params["issueTypeIds"] = issue_type_id + if query: + params["query"] = query + if page is not None: + params["page"] = page + if limit is not None: + params["maxResults"] = limit + if sort is not None: + params["sortByOptionName"] = sort + if use_all_contexts is not None: + params["useAllContexts"] = use_all_contexts + return self.get(url, params=params) + def get_custom_fields(self, search: Optional[str] = None, start: int = 1, limit: int = 50) -> T_resp_json: """ Get custom fields. Evaluated on 7.12 diff --git a/docs/jira.rst b/docs/jira.rst index 45698a013..134659c7e 100644 --- a/docs/jira.rst +++ b/docs/jira.rst @@ -217,6 +217,18 @@ Manage projects # Using " " string (space) for username gives All the active users who have browse permission for a project jira.get_users_with_browse_permission_to_a_project(username, issue_key=None, project_key=None, start=0, limit=100) + # Get existing custom fields or find by filter + jira.get_custom_fields(search=None, start=1, limit=50): + + # Returns a full representation of a Custom Field Option that has the given id. + option_id = 10001 + jira.get_custom_field_option(option_id) + + # Returns full list of Custom Field Options in a specified project. + field_id = 10000 + project_id = 1234 + jira.get_custom_field_options(field_id, project_id, issue_type_id=None) + Manage issues ------------- @@ -240,9 +252,6 @@ Manage issues value = {"name": "username"} jira.issue_field_value_append(issue_id_or_key, field, value, notify_users=True) - # Get existing custom fields or find by filter - jira.get_custom_fields(search=None, start=1, limit=50): - # Check issue exists jira.issue_exists(issue_key) From 8fec880da8fa40239f814d64364ea202e1d92fad Mon Sep 17 00:00:00 2001 From: jerubball Date: Tue, 6 Jan 2026 13:14:26 -0500 Subject: [PATCH 2/7] add get_autocomplete_suggestion method --- atlassian/jira.py | 48 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/atlassian/jira.py b/atlassian/jira.py index 4273ecabb..3450d893e 100644 --- a/atlassian/jira.py +++ b/atlassian/jira.py @@ -3964,6 +3964,54 @@ def get_priority_by_id(self, priority_id: T_id) -> T_resp_json: url = f"{base_url}/{priority_id}" return self.get(url) + def get_autocomplete_data(self) -> T_resp_json: + """ + Returns full information about visible fields that can be used in JQL. + + Available in Jira Data Center, Jira Cloud v2, Jira Cloud v3. + + Reference: https://developer.atlassian.com/server/jira/platform/rest/v11003/api-group-jql/#api-group-jql + https://developer.atlassian.com/cloud/jira/platform/rest/v2/api-group-jql/#api-group-jql + :return: + """ + url = self.resource_url("jql/autocompletedata") + return self.get(url) + + def get_autocomplete_suggestion( + self, + field_name: Optional[str] = None, + field_value: Optional[str] = None, + predicate_name: Optional[str] = None, + predicate_value: Optional[str] = None, + ) -> T_resp_json: + """ + Returns auto complete suggestions for JQL search. + + Suggestions can be obtained by providing: + + `fieldName` to get a list of all values for the field. + `fieldName` and `fieldValue` to get a list of values containing the text in `fieldValue`. + `fieldName` and `predicateName` to get a list of all predicate values for the field. + `fieldName`, `predicateName`, and `predicateValue` to get a list of predicate values containing the text in `predicateValue`. + + :param field_name: str, Optional - The field name for which the suggestions are generated. + :param field_value: str, Optional - The portion of the field value that has already been provided by the user. + :param predicate_name: str, Optional - The predicate for which the suggestions are generated. Suggestions are generated only for: "by", "from" and "to". + :param predicate_value: str, Optional - The portion of the predicate value that has already been provided by the user. + :return: + """ + url = self.resource_url("jql/autocompletedata/suggestions") + params: dict = {} + if field_name: + params["fieldName"] = field_name + if field_value: + params["fieldValue"] = field_value + if predicate_name: + params["predicateName"] = predicate_name + if predicate_value: + params["predicateValue"] = predicate_value + return self.get(url, params=params) + """ Workflow Reference: https://docs.atlassian.com/software/jira/docs/api/REST/8.5.0/#api/2/workflow From 10cafc5effc54cca6e682f356e9a65ff507f4568 Mon Sep 17 00:00:00 2001 From: jerubball Date: Tue, 6 Jan 2026 13:26:21 -0500 Subject: [PATCH 3/7] update docstrings --- atlassian/jira.py | 12 +++++++++--- docs/jira.rst | 7 +++++++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/atlassian/jira.py b/atlassian/jira.py index 3450d893e..fec5290d2 100644 --- a/atlassian/jira.py +++ b/atlassian/jira.py @@ -3966,12 +3966,12 @@ def get_priority_by_id(self, priority_id: T_id) -> T_resp_json: def get_autocomplete_data(self) -> T_resp_json: """ - Returns full information about visible fields that can be used in JQL. + Returns full information about visible fields that can be autocompleted in JQL. Available in Jira Data Center, Jira Cloud v2, Jira Cloud v3. - Reference: https://developer.atlassian.com/server/jira/platform/rest/v11003/api-group-jql/#api-group-jql - https://developer.atlassian.com/cloud/jira/platform/rest/v2/api-group-jql/#api-group-jql + Reference: https://developer.atlassian.com/server/jira/platform/rest/v11003/api-group-jql/#api-api-2-jql-autocompletedata-get + https://developer.atlassian.com/cloud/jira/platform/rest/v2/api-group-jql/#api-rest-api-2-jql-autocompletedata-get :return: """ url = self.resource_url("jql/autocompletedata") @@ -3994,6 +3994,12 @@ def get_autocomplete_suggestion( `fieldName` and `predicateName` to get a list of all predicate values for the field. `fieldName`, `predicateName`, and `predicateValue` to get a list of predicate values containing the text in `predicateValue`. + Although auto complete suggestion can be used to retrieve possible option for a field, + it may be more appropriate to use `get_custom_field_options()` method to get project-specific options for a field. + + Reference: https://developer.atlassian.com/server/jira/platform/rest/v11003/api-group-jql/#api-api-2-jql-autocompletedata-suggestions-get + https://developer.atlassian.com/cloud/jira/platform/rest/v2/api-group-jql/#api-rest-api-2-jql-autocompletedata-suggestions-get + :param field_name: str, Optional - The field name for which the suggestions are generated. :param field_value: str, Optional - The portion of the field value that has already been provided by the user. :param predicate_name: str, Optional - The predicate for which the suggestions are generated. Suggestions are generated only for: "by", "from" and "to". diff --git a/docs/jira.rst b/docs/jira.rst index 134659c7e..40724207e 100644 --- a/docs/jira.rst +++ b/docs/jira.rst @@ -467,6 +467,13 @@ Epic Issues # By default, the returned issues are ordered by rank. jira.get_issues_for_epic(board_id, epic_id, jql="", validate_query="", fields="*all", expand="", start=0, limit=50, ) + # Returns full information about visible fields that can be autocompleted in JQL. + jira.get_autocomplete_data() + + # Returns auto complete suggestions for JQL search. + field_name = "Custom Field" + jira.get_autocomplete_suggestion(field_name, field_value=None, predicate_name=None, predicate_value=None) + Manage Boards ------------- From 54abe9267378fd6e0df298f6f758af44a00e3e92 Mon Sep 17 00:00:00 2001 From: jerubball Date: Tue, 6 Jan 2026 13:30:32 -0500 Subject: [PATCH 4/7] update docstrings --- docs/jira.rst | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/jira.rst b/docs/jira.rst index 40724207e..5958e1d3a 100644 --- a/docs/jira.rst +++ b/docs/jira.rst @@ -436,6 +436,13 @@ Manage issues # :return: list of dictionaries containing the tree structure. Dictionary element contains a key (parent issue) and value (child issue). jira.get_issue_tree_recursive(issue_key, tree=[], depth=0) + # Returns full information about visible fields that can be autocompleted in JQL. + jira.get_autocomplete_data() + + # Returns auto complete suggestions for JQL search. + field_name = "Custom Field" + jira.get_autocomplete_suggestion(field_name, field_value=None, predicate_name=None, predicate_value=None) + Epic Issues ------------- @@ -467,13 +474,6 @@ Epic Issues # By default, the returned issues are ordered by rank. jira.get_issues_for_epic(board_id, epic_id, jql="", validate_query="", fields="*all", expand="", start=0, limit=50, ) - # Returns full information about visible fields that can be autocompleted in JQL. - jira.get_autocomplete_data() - - # Returns auto complete suggestions for JQL search. - field_name = "Custom Field" - jira.get_autocomplete_suggestion(field_name, field_value=None, predicate_name=None, predicate_value=None) - Manage Boards ------------- From 59a18e1a8afba3ae7a74ab19c7c48d1264e756e3 Mon Sep 17 00:00:00 2001 From: jerubball Date: Tue, 6 Jan 2026 13:43:45 -0500 Subject: [PATCH 5/7] fix type annotation for Python 3.9 compatibility --- atlassian/jira.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/atlassian/jira.py b/atlassian/jira.py index fec5290d2..b814d1aac 100644 --- a/atlassian/jira.py +++ b/atlassian/jira.py @@ -717,7 +717,7 @@ def get_custom_field_options( self, field_id: T_id, project_id: T_id, - issue_type_id: Optional[T_id | list[T_id]] = None, + issue_type_id: Union[T_id, list[T_id], None] = None, query: Optional[str] = None, page: Optional[int] = None, limit: Optional[int] = None, From 6e0f2a592f162516cab002f7d12007cc816ae22d Mon Sep 17 00:00:00 2001 From: jerubball Date: Tue, 6 Jan 2026 13:44:17 -0500 Subject: [PATCH 6/7] run black formatter --- atlassian/rest_client.py | 7 ++++++- tests/test_rest_client.py | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/atlassian/rest_client.py b/atlassian/rest_client.py index 73a1619b2..79479457d 100644 --- a/atlassian/rest_client.py +++ b/atlassian/rest_client.py @@ -359,7 +359,12 @@ def _handle(response): delay = self._parse_retry_after_header(response.headers.get("Retry-After")) if delay is not None: retry_with_header_count += 1 - log.debug("Retrying after %s seconds (attempt %d/%d)", delay, retry_with_header_count, max_retry_with_header_attempts) + log.debug( + "Retrying after %s seconds (attempt %d/%d)", + delay, + retry_with_header_count, + max_retry_with_header_attempts, + ) time.sleep(delay) return True diff --git a/tests/test_rest_client.py b/tests/test_rest_client.py index e91d0e81c..8df253d8c 100644 --- a/tests/test_rest_client.py +++ b/tests/test_rest_client.py @@ -421,6 +421,7 @@ def fake_sleep(delay): def test_retry_handler_skips_invalid_header(self, monkeypatch): """Ensure invalid Retry-After headers fall back to regular logic.""" + def fake_sleep(_): raise AssertionError("sleep should not be called for invalid header") From dc41ea469b535cdc6a7e242cc5f54116fc35662a Mon Sep 17 00:00:00 2001 From: jerubball Date: Tue, 6 Jan 2026 13:52:02 -0500 Subject: [PATCH 7/7] Fix type annotation for mypy --- atlassian/jira.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/atlassian/jira.py b/atlassian/jira.py index b814d1aac..2c09dc098 100644 --- a/atlassian/jira.py +++ b/atlassian/jira.py @@ -717,7 +717,7 @@ def get_custom_field_options( self, field_id: T_id, project_id: T_id, - issue_type_id: Union[T_id, list[T_id], None] = None, + issue_type_id: Union[T_id, list[str], None] = None, query: Optional[str] = None, page: Optional[int] = None, limit: Optional[int] = None,