# Copyright (c) 2025 Patrick Motsch # All rights reserved. """ Connect JIRA action for JIRA operations. Connects to JIRA instance and creates ticket interface. """ import logging import json import uuid from typing import Dict, Any from modules.workflows.methods.methodBase import action from modules.datamodels.datamodelChat import ActionResult, ActionDocument from modules.shared.configuration import APP_CONFIG logger = logging.getLogger(__name__) @action async def connectJira(self, parameters: Dict[str, Any]) -> ActionResult: """ Connect to JIRA instance and create ticket interface. Parameters: - apiUsername (str, required): JIRA API username/email - apiTokenConfigKey (str, required): APP_CONFIG key name for JIRA API token - apiUrl (str, required): JIRA instance URL (e.g., https://example.atlassian.net) - projectCode (str, required): JIRA project code (e.g., "DCS") - issueType (str, required): JIRA issue type (e.g., "Task") - taskSyncDefinition (str or dict, required): Field mapping definition as JSON string or dict Returns: - ActionResult with ActionDocument containing connection ID """ try: apiUsername = parameters.get("apiUsername") if not apiUsername: return ActionResult.isFailure(error="apiUsername parameter is required") apiTokenConfigKey = parameters.get("apiTokenConfigKey") if not apiTokenConfigKey: return ActionResult.isFailure(error="apiTokenConfigKey parameter is required") apiUrl = parameters.get("apiUrl") if not apiUrl: return ActionResult.isFailure(error="apiUrl parameter is required") projectCode = parameters.get("projectCode") if not projectCode: return ActionResult.isFailure(error="projectCode parameter is required") issueType = parameters.get("issueType") if not issueType: return ActionResult.isFailure(error="issueType parameter is required") taskSyncDefinitionParam = parameters.get("taskSyncDefinition") if not taskSyncDefinitionParam: return ActionResult.isFailure(error="taskSyncDefinition parameter is required") # Parse taskSyncDefinition if isinstance(taskSyncDefinitionParam, str): try: taskSyncDefinition = json.loads(taskSyncDefinitionParam) except json.JSONDecodeError as e: return ActionResult.isFailure(error=f"taskSyncDefinition is not valid JSON: {str(e)}") elif isinstance(taskSyncDefinitionParam, dict): taskSyncDefinition = taskSyncDefinitionParam else: return ActionResult.isFailure(error=f"taskSyncDefinition must be a dict or JSON string, got {type(taskSyncDefinitionParam)}") # Get API token from APP_CONFIG apiToken = APP_CONFIG.get(apiTokenConfigKey) if not apiToken: errorMsg = f"{apiTokenConfigKey} not found in APP_CONFIG" logger.error(errorMsg) return ActionResult.isFailure(error=errorMsg) # Create ticket interface syncInterface = await self.services.ticket.connectTicket( taskSyncDefinition=taskSyncDefinition, connectorType="Jira", connectorParams={ "apiUsername": apiUsername, "apiToken": apiToken, "apiUrl": apiUrl, "projectCode": projectCode, "ticketType": issueType, }, ) # Store connection with unique ID connectionId = str(uuid.uuid4()) self._connections[connectionId] = { "interface": syncInterface, "taskSyncDefinition": taskSyncDefinition, "apiUrl": apiUrl, "projectCode": projectCode, } logger.info(f"JIRA connection established: {connectionId} (Project: {projectCode})") # Generate filename workflowContext = self.services.chat.getWorkflowContext() if hasattr(self.services, 'chat') else None filename = self._generateMeaningfulFileName( "jira_connection", "json", workflowContext, "connectJira" ) # Create connection info document connectionInfo = { "connectionId": connectionId, "apiUrl": apiUrl, "projectCode": projectCode, "issueType": issueType, } validationMetadata = self._createValidationMetadata( "connectJira", connectionId=connectionId, apiUrl=apiUrl, projectCode=projectCode ) document = ActionDocument( documentName=filename, documentData=json.dumps(connectionInfo, indent=2), mimeType="application/json", validationMetadata=validationMetadata ) return ActionResult.isSuccess(documents=[document]) except Exception as e: errorMsg = f"Error connecting to JIRA: {str(e)}" logger.error(errorMsg) return ActionResult.isFailure(error=errorMsg)