Skip to content

Commit

Permalink
Merge branch 'main' into dependabot/pip/main/pytest-sugar-0.9.7
Browse files Browse the repository at this point in the history
  • Loading branch information
studioj authored Jul 24, 2023
2 parents d9c6ff2 + dd1d8a6 commit 7a8f236
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 36 deletions.
19 changes: 7 additions & 12 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
---
repos:
- repo: https://github.com/psf/black
rev: 23.3.0
hooks:
- id: black
language_version: python3
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.0
hooks:
Expand Down Expand Up @@ -32,12 +27,17 @@ repos:
require_serial: false
additional_dependencies: []
- repo: https://github.com/charliermarsh/ruff-pre-commit
rev: "v0.0.267"
rev: "v0.0.270"
hooks:
- id: ruff
args: [--fix, --exit-non-zero-on-fix]
- repo: https://github.com/psf/black # after ruff, as ruff output may need fixing
rev: 23.3.0
hooks:
- id: black
language_version: python3
- repo: https://github.com/adrienverge/yamllint
rev: v1.31.0
rev: v1.32.0
hooks:
- id: yamllint
files: \.(yaml|yml)$
Expand All @@ -50,8 +50,3 @@ repos:
- types-pkg_resources
args:
[--no-strict-optional, --ignore-missing-imports, --show-error-codes]
- repo: https://github.com/asottile/pyupgrade
rev: v3.4.0
hooks:
- id: pyupgrade
args: [--py38-plus]
6 changes: 4 additions & 2 deletions constraints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ parso==0.8.3
# via jedi
pickleshare==0.7.5
# via ipython
pillow==9.5.0
# via jira (setup.cfg)
pluggy==1.0.0
# via pytest
prompt-toolkit==3.0.38
Expand All @@ -116,7 +118,7 @@ pyjwt==2.6.0
# via
# jira (setup.cfg)
# requests-jwt
pyspnego==0.8.0
pyspnego==0.9.1
# via requests-kerberos
pytest==7.2.1
# via
Expand All @@ -129,7 +131,7 @@ pytest==7.2.1
# pytest-xdist
pytest-cache==1.0
# via jira (setup.cfg)
pytest-cov==4.0.0
pytest-cov==4.1.0
# via jira (setup.cfg)
pytest-instafail==0.4.2
# via jira (setup.cfg)
Expand Down
25 changes: 24 additions & 1 deletion docs/examples.rst
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ default address for a Jira instance started from the Atlassian Plugin SDK.

You can manually set the Jira server to use::

jac = JIRA('https://jira.atlassian.com')
jira = JIRA('https://jira.atlassian.com')

Authentication
--------------
Expand Down Expand Up @@ -264,6 +264,29 @@ Updating components::
existingComponents.append({"name" : component.name})
issue.update(fields={"components": existingComponents})

Working with Rich Text
^^^^^^^^^^^^^^^^^^^^^^

You can use rich text in an issue's description or comment. In order to use rich text, the body
content needs to be formatted using the Atlassian Document Format (ADF)::

jira = JIRA(basic_auth=("email", "API token"))
comment = {
"type": "doc",
"version": 1,
"content": [
{
"type": "codeBlock",
"content": [
{
"text": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque eget venenatis elit. Duis eu justo eget augue iaculis fermentum. Sed semper quam laoreet nisi egestas at posuere augue semper.",
"type": "text"
}
]
}
]
}
jira.add_comment("AB-123", comment)

Fields
------
Expand Down
82 changes: 61 additions & 21 deletions jira/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
import copy
import datetime
import hashlib
import imghdr
import json
import logging as _logging
import mimetypes
Expand Down Expand Up @@ -42,6 +41,7 @@

import requests
from packaging.version import parse as parse_version
from PIL import Image
from requests import Response
from requests.auth import AuthBase
from requests.structures import CaseInsensitiveDict
Expand Down Expand Up @@ -441,6 +441,7 @@ def __init__(
* access_token_secret -- OAuth access token secret to sign with the key
* consumer_key -- key of the OAuth application link defined in Jira
* key_cert -- private key file to sign requests with (should be the pair of the public key supplied to Jira in the OAuth application link)
* signature_method (Optional) -- The signature method to use with OAuth. Defaults to oauthlib.oauth1.SIGNATURE_HMAC_SHA1
kerberos (bool): True to enable Kerberos authentication. (Default: ``False``)
kerberos_options (Optional[Dict[str,str]]): A dict of properties for Kerberos authentication.
Expand Down Expand Up @@ -2341,7 +2342,9 @@ def add_watcher(self, issue: str | int, watcher: str) -> Response:
Response
"""
url = self._get_url("issue/" + str(issue) + "/watchers")
return self._session.post(url, data=json.dumps(watcher))
# Use user_id when adding watcher
watcher_id = self._get_user_id(watcher)
return self._session.post(url, data=json.dumps(watcher_id))

@translate_resource_args
def remove_watcher(self, issue: str | int, watcher: str) -> Response:
Expand Down Expand Up @@ -3688,17 +3691,38 @@ def _create_http_basic_session(self, username: str, password: str):
self._session.auth = (username, password)

def _create_oauth_session(self, oauth: dict[str, Any]):
from oauthlib.oauth1 import SIGNATURE_HMAC_SHA1
from oauthlib.oauth1 import SIGNATURE_HMAC_SHA1 as DEFAULT_SHA
from requests_oauthlib import OAuth1

oauth_instance = OAuth1(
oauth["consumer_key"],
rsa_key=oauth["key_cert"],
signature_method=SIGNATURE_HMAC_SHA1,
resource_owner_key=oauth["access_token"],
resource_owner_secret=oauth["access_token_secret"],
)
self._session.auth = oauth_instance
try:
from oauthlib.oauth1 import SIGNATURE_RSA as FALLBACK_SHA
except ImportError:
FALLBACK_SHA = DEFAULT_SHA
_logging.debug("Fallback SHA 'SIGNATURE_RSA_SHA1' could not be imported.")

for sha_type in (oauth.get("signature_method"), DEFAULT_SHA, FALLBACK_SHA):
if sha_type is None:
continue
oauth_instance = OAuth1(
oauth["consumer_key"],
rsa_key=oauth["key_cert"],
signature_method=sha_type,
resource_owner_key=oauth["access_token"],
resource_owner_secret=oauth["access_token_secret"],
)
self._session.auth = oauth_instance
try:
self.myself()
_logging.debug(f"OAuth1 succeeded with signature_method={sha_type}")
return # successful response, return with happy session
except JIRAError:
_logging.exception(
f"Failed to create OAuth session with signature_method={sha_type}.\n"
+ "Attempting fallback method(s)."
+ "Consider specifying the signature via oauth['signature_method']."
)
if sha_type is FALLBACK_SHA:
raise # We have exhausted our options, bubble up exception

def _create_kerberos_session(
self,
Expand Down Expand Up @@ -3897,7 +3921,7 @@ def _get_mime_type(self, buff: bytes) -> str | None:
if self._magic is not None:
return self._magic.id_buffer(buff)
try:
return mimetypes.guess_type("f." + str(imghdr.what(0, buff)))[0]
return mimetypes.guess_type("f." + Image.open(buff).format)[0]
except (OSError, TypeError):
self.log.warning(
"Couldn't detect content type of avatar image"
Expand Down Expand Up @@ -4377,23 +4401,33 @@ def create_project(
assignee: str = None,
ptype: str = "software",
template_name: str = None,
avatarId=None,
issueSecurityScheme=None,
permissionScheme=None,
projectCategory=None,
notificationScheme=10000,
categoryId=None,
avatarId: int = None,
issueSecurityScheme: int = None,
permissionScheme: int = None,
projectCategory: int = None,
notificationScheme: int = 10000,
categoryId: int = None,
url: str = "",
):
"""Create a project with the specified parameters.
Args:
key (str): Mandatory. Must match Jira project key requirements, usually only 2-10 uppercase characters.
name (Optional[str]): If not specified it will use the key value.
assignee (Optional[str]): key of the lead, if not specified it will use current user.
ptype (Optional[str]): Determines the type of project should be created.
template_name (Optional[str]): is used to create a project based on one of the existing project templates.
assignee (Optional[str]): Key of the lead, if not specified it will use current user.
ptype (Optional[str]): Determines the type of project that should be created. Defaults to 'software'.
template_name (Optional[str]): Is used to create a project based on one of the existing project templates.
If `template_name` is not specified, then it should use one of the default values.
avatarId (Optional[int]): ID of the avatar to use for the project.
issueSecurityScheme (Optional[int]): Determines the security scheme to use. If none provided, will fetch the
scheme named 'Default' or the first scheme returned.
permissionScheme (Optional[int]): Determines the permission scheme to use. If none provided, will fetch the
scheme named 'Default Permission Scheme' or the first scheme returned.
projectCategory (Optional[int]): Determines the category the project belongs to. If none provided,
will fetch the one named 'Default' or the first category returned.
notificationScheme (Optional[int]): Determines the notification scheme to use.
categoryId (Optional[int]): Same as projectCategory. Can be used interchangeably.
url (Optional[string]): A link to information about the project, such as documentation.
Returns:
Union[bool,int]: Should evaluate to False if it fails otherwise it will be the new project id.
Expand Down Expand Up @@ -4425,6 +4459,12 @@ def create_project(
if issueSecurityScheme is None and ps_list:
issueSecurityScheme = ps_list[0]["id"]

# If categoryId provided instead of projectCategory, attribute the categoryId value
# to the projectCategory variable
projectCategory = (
categoryId if categoryId and not projectCategory else projectCategory
)

if projectCategory is None:
ps_list = self.projectcategories()
for sec in ps_list:
Expand Down
1 change: 1 addition & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ zip_safe = False
install_requires =
defusedxml
packaging
Pillow>=2.1.0
requests-oauthlib>=1.1.0
requests>=2.10.0
requests_toolbelt
Expand Down

0 comments on commit 7a8f236

Please sign in to comment.