# Copyright (c) 2025 Patrick Motsch # All rights reserved. import logging from typing import Dict, Any from modules.workflows.methods.methodBase import MethodBase from modules.datamodels.datamodelWorkflowActions import WorkflowActionDefinition, WorkflowActionParameter from modules.shared.frontendTypes import FrontendType # Import helpers from .helpers.adfConverter import AdfConverterHelper from .helpers.documentParsing import DocumentParsingHelper # Import actions from .actions.connectJira import connectJira from .actions.exportTicketsAsJson import exportTicketsAsJson from .actions.importTicketsFromJson import importTicketsFromJson from .actions.mergeTicketData import mergeTicketData from .actions.parseCsvContent import parseCsvContent from .actions.parseExcelContent import parseExcelContent from .actions.createCsvContent import createCsvContent from .actions.createExcelContent import createExcelContent logger = logging.getLogger(__name__) class MethodJira(MethodBase): """JIRA operations methods.""" def __init__(self, services): super().__init__(services) self.name = "jira" self.description = "JIRA operations methods" # Store connections in memory (keyed by connectionId) self._connections: Dict[str, Any] = {} # Initialize helper modules self.adfConverter = AdfConverterHelper(self) self.documentParsing = DocumentParsingHelper(self) # RBAC-Integration: Action-Definitionen mit actionId self._actions = { "connectJira": WorkflowActionDefinition( actionId="jira.connectJira", description="Connect to JIRA instance and create ticket interface", parameters={ "apiUsername": WorkflowActionParameter( name="apiUsername", type="str", frontendType=FrontendType.EMAIL, required=True, description="JIRA API username/email" ), "apiTokenConfigKey": WorkflowActionParameter( name="apiTokenConfigKey", type="str", frontendType=FrontendType.TEXT, required=True, description="APP_CONFIG key name for JIRA API token" ), "apiUrl": WorkflowActionParameter( name="apiUrl", type="str", frontendType=FrontendType.TEXT, required=True, description="JIRA instance URL (e.g., https://example.atlassian.net)" ), "projectCode": WorkflowActionParameter( name="projectCode", type="str", frontendType=FrontendType.TEXT, required=True, description="JIRA project code (e.g., DCS)" ), "issueType": WorkflowActionParameter( name="issueType", type="str", frontendType=FrontendType.TEXT, required=True, description="JIRA issue type (e.g., Task)" ), "taskSyncDefinition": WorkflowActionParameter( name="taskSyncDefinition", type="str", frontendType=FrontendType.TEXTAREA, required=True, description="Field mapping definition as JSON string or dict" ) }, execute=connectJira.__get__(self, self.__class__) ), "exportTicketsAsJson": WorkflowActionDefinition( actionId="jira.exportTicketsAsJson", description="Export tickets from JIRA as JSON list", parameters={ "connectionId": WorkflowActionParameter( name="connectionId", type="str", frontendType=FrontendType.TEXT, required=True, description="Connection ID from connectJira action result" ), "taskSyncDefinition": WorkflowActionParameter( name="taskSyncDefinition", type="str", frontendType=FrontendType.TEXTAREA, required=False, description="Field mapping definition (if not provided, uses stored definition)" ) }, execute=exportTicketsAsJson.__get__(self, self.__class__) ), "importTicketsFromJson": WorkflowActionDefinition( actionId="jira.importTicketsFromJson", description="Import ticket data from JSON back to JIRA", parameters={ "connectionId": WorkflowActionParameter( name="connectionId", type="str", frontendType=FrontendType.TEXT, required=True, description="Connection ID from connectJira action result" ), "ticketData": WorkflowActionParameter( name="ticketData", type="str", frontendType=FrontendType.DOCUMENT_REFERENCE, required=True, description="Document reference containing ticket data as JSON" ), "taskSyncDefinition": WorkflowActionParameter( name="taskSyncDefinition", type="str", frontendType=FrontendType.TEXTAREA, required=False, description="Field mapping definition (if not provided, uses stored definition)" ) }, execute=importTicketsFromJson.__get__(self, self.__class__) ), "mergeTicketData": WorkflowActionDefinition( actionId="jira.mergeTicketData", description="Merge JIRA export data with existing SharePoint data", parameters={ "jiraData": WorkflowActionParameter( name="jiraData", type="str", frontendType=FrontendType.DOCUMENT_REFERENCE, required=True, description="Document reference containing JIRA ticket data as JSON array" ), "existingData": WorkflowActionParameter( name="existingData", type="str", frontendType=FrontendType.DOCUMENT_REFERENCE, required=True, description="Document reference containing existing SharePoint data as JSON array" ), "taskSyncDefinition": WorkflowActionParameter( name="taskSyncDefinition", type="str", frontendType=FrontendType.TEXTAREA, required=True, description="Field mapping definition" ), "idField": WorkflowActionParameter( name="idField", type="str", frontendType=FrontendType.TEXT, required=False, default="ID", description="Field name to use as ID for merging" ) }, execute=mergeTicketData.__get__(self, self.__class__) ), "parseCsvContent": WorkflowActionDefinition( actionId="jira.parseCsvContent", description="Parse CSV content with custom headers", parameters={ "csvContent": WorkflowActionParameter( name="csvContent", type="str", frontendType=FrontendType.DOCUMENT_REFERENCE, required=True, description="Document reference containing CSV file content as bytes" ), "skipRows": WorkflowActionParameter( name="skipRows", type="int", frontendType=FrontendType.NUMBER, required=False, default=2, description="Number of header rows to skip", validation={"min": 0, "max": 100} ), "hasCustomHeaders": WorkflowActionParameter( name="hasCustomHeaders", type="bool", frontendType=FrontendType.CHECKBOX, required=False, default=True, description="Whether CSV has custom header rows" ) }, execute=parseCsvContent.__get__(self, self.__class__) ), "parseExcelContent": WorkflowActionDefinition( actionId="jira.parseExcelContent", description="Parse Excel content with custom headers", parameters={ "excelContent": WorkflowActionParameter( name="excelContent", type="str", frontendType=FrontendType.DOCUMENT_REFERENCE, required=True, description="Document reference containing Excel file content as bytes" ), "skipRows": WorkflowActionParameter( name="skipRows", type="int", frontendType=FrontendType.NUMBER, required=False, default=3, description="Number of header rows to skip", validation={"min": 0, "max": 100} ), "hasCustomHeaders": WorkflowActionParameter( name="hasCustomHeaders", type="bool", frontendType=FrontendType.CHECKBOX, required=False, default=True, description="Whether Excel has custom header rows" ) }, execute=parseExcelContent.__get__(self, self.__class__) ), "createCsvContent": WorkflowActionDefinition( actionId="jira.createCsvContent", description="Create CSV content with custom headers", parameters={ "data": WorkflowActionParameter( name="data", type="str", frontendType=FrontendType.DOCUMENT_REFERENCE, required=True, description="Document reference containing data as JSON (with data field from mergeTicketData)" ), "headers": WorkflowActionParameter( name="headers", type="str", frontendType=FrontendType.DOCUMENT_REFERENCE, required=False, description="Document reference containing headers JSON (from parseCsvContent/parseExcelContent)" ), "columns": WorkflowActionParameter( name="columns", type="List[str]", frontendType=FrontendType.MULTISELECT, required=False, description="List of column names (if not provided, extracted from taskSyncDefinition or data)" ), "taskSyncDefinition": WorkflowActionParameter( name="taskSyncDefinition", type="str", frontendType=FrontendType.TEXTAREA, required=False, description="Field mapping definition (used to extract column names if columns not provided)" ) }, execute=createCsvContent.__get__(self, self.__class__) ), "createExcelContent": WorkflowActionDefinition( actionId="jira.createExcelContent", description="Create Excel content with custom headers", parameters={ "data": WorkflowActionParameter( name="data", type="str", frontendType=FrontendType.DOCUMENT_REFERENCE, required=True, description="Document reference containing data as JSON (with data field from mergeTicketData)" ), "headers": WorkflowActionParameter( name="headers", type="str", frontendType=FrontendType.DOCUMENT_REFERENCE, required=False, description="Document reference containing headers JSON (from parseExcelContent)" ), "columns": WorkflowActionParameter( name="columns", type="List[str]", frontendType=FrontendType.MULTISELECT, required=False, description="List of column names (if not provided, extracted from taskSyncDefinition or data)" ), "taskSyncDefinition": WorkflowActionParameter( name="taskSyncDefinition", type="str", frontendType=FrontendType.TEXTAREA, required=False, description="Field mapping definition (used to extract column names if columns not provided)" ) }, execute=createExcelContent.__get__(self, self.__class__) ) } # Validate actions after definition self._validateActions() # Register actions as methods (optional, für direkten Zugriff) self.connectJira = connectJira.__get__(self, self.__class__) self.exportTicketsAsJson = exportTicketsAsJson.__get__(self, self.__class__) self.importTicketsFromJson = importTicketsFromJson.__get__(self, self.__class__) self.mergeTicketData = mergeTicketData.__get__(self, self.__class__) self.parseCsvContent = parseCsvContent.__get__(self, self.__class__) self.parseExcelContent = parseExcelContent.__get__(self, self.__class__) self.createCsvContent = createCsvContent.__get__(self, self.__class__) self.createExcelContent = createExcelContent.__get__(self, self.__class__)