231 lines
9.8 KiB
Python
231 lines
9.8 KiB
Python
"""
|
|
Delta Group JIRA-SharePoint Sync Manager
|
|
|
|
This module handles the synchronization of JIRA tickets to SharePoint using the new
|
|
Graph API-based connector architecture.
|
|
"""
|
|
|
|
import logging
|
|
import csv
|
|
import io
|
|
from datetime import datetime, UTC
|
|
from typing import Dict, Any, List, Optional
|
|
from modules.connectors.connectorSharepoint import ConnectorSharepoint
|
|
from modules.connectors.connectorTicketJira import ConnectorTicketJira
|
|
from modules.interfaces.interfaceAppObjects import getRootInterface
|
|
from modules.interfaces.interfaceAppModel import UserInDB
|
|
from modules.interfaces.interfaceTicketObjects import TicketSharepointSyncInterface
|
|
from modules.shared.timezoneUtils import get_utc_timestamp
|
|
from modules.shared.configuration import APP_CONFIG
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
# Get environment type from configuration
|
|
APP_ENV_TYPE = APP_CONFIG.get("APP_ENV_TYPE", "dev")
|
|
|
|
|
|
class ManagerSyncDelta:
|
|
"""Manages JIRA to SharePoint synchronization for Delta Group."""
|
|
#SHAREPOINT_SITE_ID = "02830618-4029-4dc8-8d3d-f5168f282249"
|
|
#SHAREPOINT_SITE_NAME = "SteeringBPM"
|
|
#SHAREPOINT_MAIN_FOLDER = "/sites/SteeringBPM/Freigegebene Dokumente/General/50 Docs hosted by SELISE"
|
|
#SHAREPOINT_BACKUP_FOLDER = "/sites/SteeringBPM/Freigegebene Dokumente/General/50 Docs hosted by SELISE/SyncHistory"
|
|
#SHAREPOINT_AUDIT_FOLDER = "/sites/SteeringBPM/Freigegebene Dokumente/General/50 Docs hosted by SELISE/SyncHistory"
|
|
|
|
# SharePoint site constants using hostname + site path (resolve real site ID at runtime)
|
|
SHAREPOINT_HOSTNAME = "pcuster.sharepoint.com"
|
|
SHAREPOINT_SITE_PATH = "KM.DELTAG.20968511411"
|
|
SHAREPOINT_SITE_NAME = "KM.DELTAG.20968511411"
|
|
# Drive-relative (document library) paths, not server-relative "/sites/..."
|
|
# Note: Default library name is "Shared Documents" in Graph
|
|
SHAREPOINT_MAIN_FOLDER = "1_Arbeitsbereich"
|
|
SHAREPOINT_BACKUP_FOLDER = "1_Arbeitsbereich/SyncHistory"
|
|
SHAREPOINT_AUDIT_FOLDER = "1_Arbeitsbereich/SyncHistory"
|
|
|
|
# Fixed filename for the main CSV file (like original synchronizer)
|
|
SYNC_FILE_NAME = "DELTAgroup x SELISE Ticket Exchange List.csv"
|
|
|
|
# JIRA connection parameters (hardcoded for Delta Group)
|
|
JIRA_USERNAME = "p.motsch@valueon.ch"
|
|
JIRA_API_TOKEN = "ATATT3xFfGF0d973nNb3R1wTDI4lesmJfJAmooS-4cYMJTyLfwYv4himrE6yyCxyX3aSMfl34NHcm2fAXeFXrLHUzJx0RQVUBonCFnlgexjLQTgS5BoCbSO7dwAVjlcHZZkArHbooCUaRwJ15n6AHkm-nwdjLQ3Z74TFnKKUZC4uhuh3Aj-MuX8=2D7124FA"
|
|
JIRA_URL = "https://deltasecurity.atlassian.net"
|
|
JIRA_PROJECT_CODE = "DCS"
|
|
JIRA_ISSUE_TYPE = "Task"
|
|
|
|
# Task sync definition for field mapping (like original synchronizer)
|
|
TASK_SYNC_DEFINITION = {
|
|
"ID": ["get", ["key"]],
|
|
"Summary": ["get", ["fields", "summary"]],
|
|
"Status": ["get", ["fields", "status", "name"]],
|
|
"Assignee": ["get", ["fields", "assignee", "displayName"]],
|
|
"Reporter": ["get", ["fields", "reporter", "displayName"]],
|
|
"Created": ["get", ["fields", "created"]],
|
|
"Updated": ["get", ["fields", "updated"]],
|
|
"Priority": ["get", ["fields", "priority", "name"]],
|
|
"IssueType": ["get", ["fields", "issuetype", "name"]],
|
|
"Project": ["get", ["fields", "project", "name"]],
|
|
"Description": ["get", ["fields", "description"]],
|
|
}
|
|
|
|
def __init__(self):
|
|
"""Initialize the sync manager with hardcoded Delta Group credentials."""
|
|
self.root_interface = getRootInterface()
|
|
self.jira_connector = None
|
|
self.sharepoint_connector = None
|
|
self.target_site = None
|
|
|
|
async def initialize_connectors(self) -> bool:
|
|
"""Initialize JIRA and SharePoint connectors."""
|
|
try:
|
|
logger.info("Initializing JIRA connector with hardcoded credentials")
|
|
|
|
# Initialize JIRA connector using class constants
|
|
self.jira_connector = await ConnectorTicketJira.create(
|
|
jira_username=self.JIRA_USERNAME,
|
|
jira_api_token=self.JIRA_API_TOKEN,
|
|
jira_url=self.JIRA_URL,
|
|
project_code=self.JIRA_PROJECT_CODE,
|
|
issue_type=self.JIRA_ISSUE_TYPE
|
|
)
|
|
|
|
# Use the current logged-in user from root interface
|
|
activeUser = self.root_interface.currentUser
|
|
if not activeUser:
|
|
logger.error("No current user available - SharePoint connection required")
|
|
return False
|
|
|
|
logger.info(f"Using current user for SharePoint: {activeUser.id}")
|
|
|
|
# Get SharePoint connection for this user
|
|
user_connections = self.root_interface.getUserConnections(activeUser.id)
|
|
sharepoint_connection = None
|
|
|
|
for connection in user_connections:
|
|
if connection.authority == "msft":
|
|
sharepoint_connection = connection
|
|
break
|
|
|
|
if not sharepoint_connection:
|
|
logger.error("No SharePoint connection found for Delta Group user")
|
|
return False
|
|
|
|
logger.info(f"Found SharePoint connection: {sharepoint_connection.id}")
|
|
|
|
# Get SharePoint token for this connection
|
|
sharepoint_token = self.root_interface.getConnectionToken(sharepoint_connection.id)
|
|
if not sharepoint_token:
|
|
logger.error("No SharePoint token found for Delta Group user connection")
|
|
return False
|
|
|
|
logger.info(f"Found SharePoint token: {sharepoint_token.id}")
|
|
|
|
# Initialize SharePoint connector with Graph API
|
|
self.sharepoint_connector = ConnectorSharepoint(access_token=sharepoint_token.tokenAccess)
|
|
|
|
# Resolve the site by hostname + site path to get the real site ID
|
|
logger.info(
|
|
f"Resolving site ID via hostname+path: {self.SHAREPOINT_HOSTNAME}:/sites/{self.SHAREPOINT_SITE_PATH}"
|
|
)
|
|
resolved = await self.sharepoint_connector.find_site_by_url(
|
|
hostname=self.SHAREPOINT_HOSTNAME,
|
|
site_path=self.SHAREPOINT_SITE_PATH
|
|
)
|
|
|
|
if not resolved:
|
|
logger.error(
|
|
f"Failed to resolve site. Hostname: {self.SHAREPOINT_HOSTNAME}, Path: {self.SHAREPOINT_SITE_PATH}"
|
|
)
|
|
return False
|
|
|
|
self.target_site = {
|
|
"id": resolved.get("id"),
|
|
"displayName": resolved.get("displayName", self.SHAREPOINT_SITE_NAME),
|
|
"name": resolved.get("name", self.SHAREPOINT_SITE_NAME)
|
|
}
|
|
|
|
# Test site access by listing root of the drive
|
|
logger.info("Testing site access using resolved site ID...")
|
|
test_result = await self.sharepoint_connector.list_folder_contents(
|
|
site_id=self.target_site["id"],
|
|
folder_path=""
|
|
)
|
|
|
|
if test_result is not None:
|
|
logger.info(
|
|
f"Site access confirmed: {self.target_site['displayName']} (ID: {self.target_site['id']})"
|
|
)
|
|
else:
|
|
logger.error("Could not access site drive - check permissions")
|
|
return False
|
|
|
|
return True
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error initializing connectors: {str(e)}")
|
|
return False
|
|
|
|
async def sync_jira_to_sharepoint(self) -> bool:
|
|
"""Perform the main JIRA to SharePoint synchronization using sophisticated sync logic."""
|
|
try:
|
|
logger.info("Starting JIRA to SharePoint synchronization")
|
|
|
|
# Initialize connectors
|
|
if not await self.initialize_connectors():
|
|
logger.error("Failed to initialize connectors")
|
|
return False
|
|
|
|
# Create the sophisticated sync interface
|
|
sync_interface = await TicketSharepointSyncInterface.create(
|
|
connector_ticket=self.jira_connector,
|
|
connector_sharepoint=self.sharepoint_connector,
|
|
task_sync_definition=self.TASK_SYNC_DEFINITION,
|
|
sync_folder=self.SHAREPOINT_MAIN_FOLDER,
|
|
sync_file=self.SYNC_FILE_NAME,
|
|
backup_folder=self.SHAREPOINT_BACKUP_FOLDER,
|
|
audit_folder=self.SHAREPOINT_AUDIT_FOLDER,
|
|
site_id=self.target_site['id']
|
|
)
|
|
|
|
# Perform the sophisticated sync
|
|
logger.info("Performing sophisticated JIRA to CSV sync...")
|
|
await sync_interface.sync_from_jira_to_csv()
|
|
|
|
logger.info("JIRA to SharePoint synchronization completed successfully")
|
|
return True
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error during JIRA to SharePoint synchronization: {str(e)}")
|
|
return False
|
|
|
|
|
|
|
|
# Global sync function for use in app.py
|
|
async def perform_sync_jira_delta_group() -> bool:
|
|
"""Perform JIRA to SharePoint synchronization for Delta Group.
|
|
|
|
This function is called by the scheduler and can be used independently.
|
|
|
|
Returns:
|
|
bool: True if synchronization was successful, False otherwise
|
|
"""
|
|
try:
|
|
if APP_ENV_TYPE != "TASK-ACTIVATE-WHEN-ACCOUNT-READY-prod":
|
|
logger.info("JIRA to SharePoint synchronization: TASK to run only in PROD")
|
|
return True
|
|
|
|
logger.info("Starting Delta Group JIRA sync...")
|
|
|
|
|
|
sync_manager = ManagerSyncDelta()
|
|
success = await sync_manager.sync_jira_to_sharepoint()
|
|
|
|
if success:
|
|
logger.info("Delta Group JIRA sync completed successfully")
|
|
else:
|
|
logger.error("Delta Group JIRA sync failed")
|
|
|
|
return success
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error in perform_sync_jira_delta_group: {str(e)}")
|
|
return False
|