diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0548e66..78de725 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -67,7 +67,7 @@ repos: hooks: - id: mypy files: ^surfsense_backend/ - additional_dependencies: [] + additional_dependencies: ['types-requests'] args: [--ignore-missing-imports, --disallow-untyped-defs] - repo: https://github.com/PyCQA/bandit @@ -75,7 +75,7 @@ repos: hooks: - id: bandit files: ^surfsense_backend/ - args: ['-r', '.', '-f', 'json'] + args: ['-r', '-f', 'json'] exclude: ^surfsense_backend/(tests/|alembic/) # Frontend/Extension Hooks (TypeScript/JavaScript) diff --git a/surfsense_backend/app/connectors/jira_connector.py b/surfsense_backend/app/connectors/jira_connector.py new file mode 100644 index 0000000..7f23453 --- /dev/null +++ b/surfsense_backend/app/connectors/jira_connector.py @@ -0,0 +1,90 @@ +""" +Jira Connector Module + +A module for retrieving data from Jira. +Allows fetching issue lists and their comments, projects and more. +""" + +from typing import Any, Dict, Optional + +import requests + + +class JiraConnector: + """Class for retrieving data from Jira.""" + + def __init__( + self, + base_url: Optional[str] = None, + personal_access_token: Optional[str] = None, + ): + """ + Initialize the JiraConnector class. + + Args: + base_url: Jira instance base URL (e.g., 'https://yourcompany.atlassian.net') (optional) + personal_access_token: Jira personal access token (optional) + """ + self.base_url = base_url + self.personal_access_token = personal_access_token + self.api_version = "3" # Jira Cloud API version + + def set_personal_access_token(self, personal_access_token: str) -> None: + """ + Set the Jira personal access token. + + Args: + personal_access_token: Jira personal access token + """ + self.personal_access_token = personal_access_token + + def get_headers(self) -> Dict[str, str]: + """ + Get headers for Jira API requests. + + Returns: + Dictionary of headers + + Raises: + ValueError: If personal_access_token or base_url have not been set + """ + if not all([self.base_url, self.personal_access_token]): + raise ValueError("Jira personal access token or base URL not initialized.") + + return { + "Content-Type": "application/json", + "Authorization": f"Bearer {self.personal_access_token}", + "Accept": "application/json", + } + + def make_api_request( + self, endpoint: str, params: Optional[Dict[str, Any]] = None + ) -> Dict[str, Any]: + """ + Make a request to the Jira API. + + Args: + endpoint: API endpoint (without base URL) + params: Query parameters for the request (optional) + + Returns: + Response data from the API + + Raises: + ValueError: If personal_access_token or base_url have not been set + Exception: If the API request fails + """ + if not all([self.base_url, self.personal_access_token]): + raise ValueError("Jira personal access token or base URL not initialized.") + + url = f"{self.base_url}/rest/api/{self.api_version}/{endpoint}" + headers = self.get_headers() + + response = requests.get(url, headers=headers, params=params, timeout=500) + + if response.status_code == 200: + return response.json() + else: + raise Exception( + f"API request failed with status code {response.status_code}: {response.text}" + )