Skip to content
Merged
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
40 changes: 36 additions & 4 deletions apps/jira_utils/jira_information.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,16 +146,24 @@ def process_jira_command_line_config_file(
version_string_not_targeted_jiras: str,
target_versions: list[str],
skip_projects: list[str],
user: str,
cloud: bool | None,
) -> dict[str, Any]:
# Process all the arguments passed from command line or config file or environment variable
config_dict = get_util_config(util_name="pyutils-jira", config_file_path=config_file_path)
url = url or config_dict.get("url", "")
token = token or config_dict.get("token", "")
user = user or config_dict.get("user", "")
cloud = cloud if cloud is not None else config_dict.get("cloud", False)

if not (url and token):
LOGGER.error("Jira url and token are required.")
sys.exit(1)

if cloud and not user:
LOGGER.error("Jira user is required for cloud Jira.")
sys.exit(1)

return {
"url": url,
"token": token,
Expand All @@ -166,6 +174,8 @@ def process_jira_command_line_config_file(
),
"target_versions": target_versions or config_dict.get("target_versions", []),
"skip_project_ids": skip_projects or config_dict.get("skip_project_ids", []),
"user": user,
"cloud": cloud,
}


Expand Down Expand Up @@ -197,6 +207,12 @@ def process_jira_command_line_config_file(
type=click.STRING,
default=os.getenv("JIRA_TOKEN"),
)
@click.option(
"--user",
help="Provide the Jira user email (required for cloud Jira).",
type=click.STRING,
default=os.getenv("JIRA_USER"),
)
@click.option(
"--issue-pattern",
help="Provide the regex for Jira ids",
Expand All @@ -219,16 +235,24 @@ def process_jira_command_line_config_file(
default="vfuture",
)
@click.option("--verbose", default=False, is_flag=True)
@click.option(
"--cloud",
help="Use cloud Jira authentication (basic_auth with user email and API token).",
is_flag=True,
default=None,
)
Comment thread
rnetser marked this conversation as resolved.
def get_jira_mismatch(
config_file_path: str,
target_versions: list[str],
url: str,
token: str,
user: str,
skip_projects: list[str],
resolved_statuses: list[str],
issue_pattern: str,
version_string_not_targeted_jiras: str,
verbose: bool,
cloud: bool | None,
) -> None:
LOGGER.setLevel(logging.DEBUG if verbose else logging.INFO)
if not (config_file_path or (token and url)):
Expand All @@ -245,12 +269,20 @@ def get_jira_mismatch(
skip_projects=skip_projects,
version_string_not_targeted_jiras=version_string_not_targeted_jiras,
target_versions=target_versions,
user=user,
cloud=cloud,
)

jira_obj = JIRA(
token_auth=jira_config_dict["token"],
options={"server": jira_config_dict["url"]},
)
if jira_config_dict["cloud"]:
jira_obj = JIRA(
server=jira_config_dict["url"],
basic_auth=(jira_config_dict["user"], jira_config_dict["token"]),
)
else:
jira_obj = JIRA(
token_auth=jira_config_dict["token"],
options={"server": jira_config_dict["url"]},
)
jira_error: dict[str, str] = {}

if jira_id_dict := get_jiras_from_python_files(
Expand Down
2 changes: 2 additions & 0 deletions config.example.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ pyutils-polarion-set-automated:
pyutils-jira:
url: <jira url>
token: <jira token>
user: <jira user email>
cloud: false
resolved_statuses:
- verified
- release pending
Expand Down
2 changes: 2 additions & 0 deletions tests/jira_utils/test_jira_cfg_file.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
pyutils-jira:
url: "https://example.com"
token: ""
user: "test@example.com"
cloud: false
resolved_statuses:
- "RESOLVED"
issue_pattern: "*"
Expand Down
189 changes: 189 additions & 0 deletions tests/jira_utils/test_jira_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ def test_process_jira_command_line_config_file_valid_config(mocker):
"version_string_not_targeted_jiras": version_string_not_targeted_jiras,
"target_versions": target_versions,
"skip_project_ids": skip_projects,
"user": "",
"cloud": False,
},
)
result = process_jira_command_line_config_file(
Expand All @@ -60,6 +62,8 @@ def test_process_jira_command_line_config_file_valid_config(mocker):
version_string_not_targeted_jiras,
target_versions,
skip_projects,
user="",
cloud=False,
)
assert result == {
"url": url,
Expand All @@ -69,10 +73,195 @@ def test_process_jira_command_line_config_file_valid_config(mocker):
"not_targeted_version_str": version_string_not_targeted_jiras,
"target_versions": target_versions,
"skip_project_ids": skip_projects,
"user": "",
"cloud": False,
}
mock_get_util_config.assert_called_once()


def test_process_jira_command_line_config_file_cloud_valid(mocker):
config_file_path = "/path/to/config"
url = "https://example.com"
token = "1234567890"
user = "test@example.com"

mocker.patch(
"apps.jira_utils.jira_information.get_util_config",
return_value={
"url": url,
"token": token,
"cloud": True,
"user": user,
},
)
result = process_jira_command_line_config_file(
config_file_path=config_file_path,
url=url,
token=token,
issue_pattern="*",
resolved_statuses=["RESOLVED"],
version_string_not_targeted_jiras="v1.*",
target_versions=["v2"],
skip_projects=[],
user=user,
cloud=True,
)
assert result["cloud"] is True
assert result["user"] == user


def test_process_jira_command_line_config_file_cloud_missing_user(mocker):
config_file_path = "/path/to/config"
url = "https://example.com"
token = "1234567890"

mocker.patch(
"apps.jira_utils.jira_information.get_util_config",
return_value={
"url": url,
"token": token,
},
)
with pytest.raises(SystemExit):
process_jira_command_line_config_file(
config_file_path=config_file_path,
url=url,
token=token,
issue_pattern="*",
resolved_statuses=["RESOLVED"],
version_string_not_targeted_jiras="v1.*",
target_versions=["v2"],
skip_projects=[],
user="",
cloud=True,
)


def test_process_cloud_from_config_when_cli_not_passed(mocker):
mocker.patch(
"apps.jira_utils.jira_information.get_util_config",
return_value={"url": "https://example.com", "token": "tok", "cloud": True, "user": "u@e.com"},
)
result = process_jira_command_line_config_file(
config_file_path="/path",
url="https://example.com",
token="tok",
issue_pattern="*",
resolved_statuses=[],
version_string_not_targeted_jiras="",
target_versions=[],
skip_projects=[],
user="u@e.com",
cloud=None,
)
assert result["cloud"] is True
assert result["user"] == "u@e.com"


def test_process_cloud_defaults_false_when_not_in_config(mocker):
mocker.patch(
"apps.jira_utils.jira_information.get_util_config",
return_value={"url": "https://example.com", "token": "tok"},
)
result = process_jira_command_line_config_file(
config_file_path="/path",
url="https://example.com",
token="tok",
issue_pattern="*",
resolved_statuses=[],
version_string_not_targeted_jiras="",
target_versions=[],
skip_projects=[],
user="",
cloud=None,
)
assert result["cloud"] is False


def test_process_cloud_from_config_missing_user_exits(mocker):
mocker.patch(
"apps.jira_utils.jira_information.get_util_config",
return_value={"url": "https://example.com", "token": "tok", "cloud": True},
)
with pytest.raises(SystemExit):
process_jira_command_line_config_file(
config_file_path="/path",
url="https://example.com",
token="tok",
issue_pattern="*",
resolved_statuses=[],
version_string_not_targeted_jiras="",
target_versions=[],
skip_projects=[],
user="",
cloud=None,
)


def test_process_user_from_config_fallback(mocker):
mocker.patch(
"apps.jira_utils.jira_information.get_util_config",
return_value={"url": "https://example.com", "token": "tok", "user": "config@example.com", "cloud": True},
)
result = process_jira_command_line_config_file(
config_file_path="/path",
url="https://example.com",
token="tok",
issue_pattern="*",
resolved_statuses=[],
version_string_not_targeted_jiras="",
target_versions=[],
skip_projects=[],
user="",
cloud=True,
)
assert result["user"] == "config@example.com"
assert result["cloud"] is True


def test_process_cloud_false_explicit_ignores_config(mocker):
mocker.patch(
"apps.jira_utils.jira_information.get_util_config",
return_value={"url": "https://example.com", "token": "tok", "cloud": True},
)
result = process_jira_command_line_config_file(
config_file_path="/path",
url="https://example.com",
token="tok",
issue_pattern="*",
resolved_statuses=[],
version_string_not_targeted_jiras="",
target_versions=[],
skip_projects=[],
user="",
cloud=False,
)
assert result["cloud"] is False


def test_process_server_mode_default(mocker):
mocker.patch(
"apps.jira_utils.jira_information.get_util_config",
return_value={"url": "https://example.com", "token": "tok"},
)
result = process_jira_command_line_config_file(
config_file_path="/path",
url="https://example.com",
token="tok",
issue_pattern="([A-Z]+-[0-9]+)",
resolved_statuses=["resolved"],
version_string_not_targeted_jiras="vfuture",
target_versions=[],
skip_projects=[],
user="",
cloud=False,
)
assert result["cloud"] is False
assert result["user"] == ""
assert result["url"] == "https://example.com"
assert result["token"] == "tok"


@pytest.mark.parametrize(
"test_params",
[
Expand Down