Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 108 additions & 0 deletions atlassian/jira.py
Original file line number Diff line number Diff line change
Expand Up @@ -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: Union[T_id, list[str], None] = 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
Expand Down Expand Up @@ -3910,6 +3964,60 @@ 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 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-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")
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`.

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".
: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
Expand Down
7 changes: 6 additions & 1 deletion atlassian/rest_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
22 changes: 19 additions & 3 deletions docs/jira.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
-------------

Expand All @@ -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)

Expand Down Expand Up @@ -427,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
-------------

Expand Down
1 change: 1 addition & 0 deletions tests/test_rest_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -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")

Expand Down
Loading