diff --git a/src/components/workflows/components/WorkflowDesigner.py b/src/components/workflows/components/WorkflowDesigner.py index b23be79..5506d40 100644 --- a/src/components/workflows/components/WorkflowDesigner.py +++ b/src/components/workflows/components/WorkflowDesigner.py @@ -14,6 +14,7 @@ from components.workflows.db_management import WorkflowsDesignerSettings, Workfl Connection, WorkflowsDesignerDbManager, ComponentState from components_helpers import apply_boundaries, mk_tooltip, mk_dialog_buttons, mk_icon from core.instance_manager import InstanceManager +from core.jira import JiraRequestTypes from core.utils import get_unique_id, make_safe_id from utils.DbManagementHelper import DbManagementHelper @@ -443,13 +444,24 @@ class WorkflowDesigner(BaseComponent): @staticmethod def _mk_jira_processor_details(component): + def _mk_option(name): + return Option(name.name, + value=name.value, + selected="selected" if name.value == component.properties.get("request_type", + JiraRequestTypes.Issues) else None) + return Div( Fieldset( Legend("JQL", cls="fieldset-legend"), + Select( + *[_mk_option(enum) for enum in JiraRequestTypes], + cls="select w-xs", + name="request_type", + ), Input(type="text", - name="jira_jql", - value=component.properties.get("jira_jql", ""), - placeholder="Enter JQL", + name="request", + value=component.properties.get("request", ""), + placeholder="Enter Request (JQL, Issue ID...)", cls="input w-full"), P("Write your jsl code"), cls="fieldset bg-base-200 border-base-300 rounded-box border p-4" diff --git a/src/components/workflows/components/WorkflowPlayer.py b/src/components/workflows/components/WorkflowPlayer.py index f18d2c4..f86c0d9 100644 --- a/src/components/workflows/components/WorkflowPlayer.py +++ b/src/components/workflows/components/WorkflowPlayer.py @@ -195,8 +195,8 @@ class WorkflowPlayer(BaseComponent): JiraDataProducer(self._session, self._settings_manager, component.id, - 'issues', - component.properties["jira_jql"])) + component.properties["request_type"], + component.properties["request"])) elif key == (ProcessorTypes.Filter, "Default"): engine.add_processor(DefaultDataFilter(component.id, component.properties["filter"])) elif key == (ProcessorTypes.Presenter, "Default"): diff --git a/src/core/jira.py b/src/core/jira.py index 12df45f..a7e1741 100644 --- a/src/core/jira.py +++ b/src/core/jira.py @@ -1,12 +1,13 @@ import json import logging +from enum import Enum import requests from requests.auth import HTTPBasicAuth from core.Expando import Expando -JIRA_ROOT = "https://altares.atlassian.net/rest/api/2" +JIRA_ROOT = "https://altares.atlassian.net/rest/api/3" DEFAULT_HEADERS = {"Accept": "application/json"} logger = logging.getLogger("jql") @@ -16,6 +17,11 @@ class NotFound(Exception): pass +class JiraRequestTypes(Enum): + Issues = "issues" + Comments = "comments" + + class Jira: """Manage default operation to JIRA""" @@ -67,7 +73,7 @@ class Jira: as_dict = json.loads(response.text) return [Expando(field) for field in as_dict] - def jql(self, jql: str, fields="summary,status,assignee") -> list[Expando]: + def issues(self, jql: str, fields="summary,status,assignee") -> list[Expando]: """ Executes a JQL and returns the list of issues :param jql: @@ -91,13 +97,11 @@ class Jira: result = [] while True: logger.debug(f"Request startAt '{payload['startAt']}'") - response = requests.request( - "POST", - url, - data=json.dumps(payload), - headers=headers, - auth=self.auth - ) + response = requests.request("POST", + url, + data=json.dumps(payload), + headers=headers, + auth=self.auth) if response.status_code != 200: raise Exception(self._format_error(response)) @@ -113,6 +117,25 @@ class Jira: return [Expando(issue) for issue in result] + def comments(self, issue_id: str) -> list[Expando]: + """ + Retrieve the list of comments for an issue + :param issue_id: + :return: + """ + url = f"{JIRA_ROOT}/issue/{issue_id}/comment" + + response = requests.request("GET", + url, + headers=DEFAULT_HEADERS, + auth=self.auth) + if response.status_code != 200: + raise Exception(self._format_error(response)) + + as_dict = json.loads(response.text) + result = as_dict["comments"] + return [Expando(issue) for issue in result] + def extract(self, jql, mappings, updates=None) -> list[dict]: """ Executes a jql and returns list of dict @@ -134,7 +157,7 @@ class Jira: # retrieve the list of requested fields from what was asked in the mapping jira_fields = [_get_field(mapping) for mapping in mappings] as_string = ", ".join(jira_fields) - issues = self.jql(jql, as_string) + issues = self.issues(jql, as_string) for issue in issues: # apply updates if needed @@ -222,4 +245,4 @@ class Jira: error_messages = json.loads(response.text)["errorMessages"] else: error_messages = response.text - return f"Error {response.status_code} : {response.reason} : {error_messages}" \ No newline at end of file + return f"Error {response.status_code} : {response.reason} : {error_messages}" diff --git a/src/workflow/engine.py b/src/workflow/engine.py index 5806385..cd3d386 100644 --- a/src/workflow/engine.py +++ b/src/workflow/engine.py @@ -92,17 +92,20 @@ class TableDataProducer(DataProducer): class JiraDataProducer(DataProducer): """Base class for data producers that emit data from Jira.""" - def __init__(self, session, settings_manager, component_id, jira_object='issues', jira_query=''): + def __init__(self, session, settings_manager, component_id, request_type='issues', request=''): super().__init__(component_id) self._session = session self.settings_manager = settings_manager - self.jira_object = jira_object - self.jira_query = jira_query + self.request_type = request_type + self.request = request self.db = AdminDbManager(session, settings_manager).jira def emit(self, data: Any = None) -> Generator[Any, None, None]: jira = Jira(self.db.user_name, self.db.api_token) - yield from jira.jql(self.jira_query) + if not hasattr(jira, self.request_type): + raise ValueError(f"Invalid request type: {self.request_type}") + + yield from getattr(jira, self.request_type)(self.request) class DefaultDataFilter(DataFilter):