diff --git a/modules/connectors/connectorSharepoint.py b/modules/connectors/connectorSharepoint.py new file mode 100644 index 00000000..33e220a0 --- /dev/null +++ b/modules/connectors/connectorSharepoint.py @@ -0,0 +1,55 @@ +"""Connector for CRUD sharepoint operations.""" + +from dataclasses import dataclass +from office365.sharepoint.client_context import ClientContext + + +@dataclass +class ConnectorSharepoint: + ctx: ClientContext + + @classmethod + async def create(cls, ctx: ClientContext) -> "ConnectorSharepoint": + """Creates an instance of the Sharepoint connector. + + Params: + ctx: The ClientContext instance. + + Returns: + ConnectorSharepoint: An instance of the Sharepoint connector. + """ + return cls(ctx=ctx) + + @classmethod + def get_client_context_from_username_password( + cls, site_url: str, username: str, password: str + ) -> ClientContext: + """Creates a ClientContext instance from username and password. + + Params: + site_url: The URL of the SharePoint site. + username: The username for authentication. + password: The password for authentication. + + Returns: + ClientContext: An instance of the ClientContext. + """ + return ClientContext(site_url).with_user_credentials(username, password) + + @classmethod + def get_client_context_from_app( + cls, site_url: str, client_id: str, client_secret: str + ) -> ClientContext: + """Creates a ClientContext instance from client ID and client secret. + + Params: + site_url: The URL of the SharePoint site. + client_id: The client ID for authentication. + client_secret: The client secret for authentication. + + Returns: + ClientContext: An instance of the ClientContext. + """ + return ClientContext(site_url).with_client_credentials( + client_id=client_id, client_secret=client_secret + ) diff --git a/modules/interfaces/interfaceTicketObjects.py b/modules/interfaces/interfaceTicketObjects.py index e1ac75a8..3eb7a2bd 100644 --- a/modules/interfaces/interfaceTicketObjects.py +++ b/modules/interfaces/interfaceTicketObjects.py @@ -1,10 +1,50 @@ from dataclasses import dataclass +from shareplum import Site +from shareplum import Office365 +from shareplum.site import Version + +from modules.interfaces.interfaceTicketModel import TicketBase SUPPORTED_SYSTEMS = ["jira"] @dataclass(slots=True) -class TicketInterface: - # TODO: user must create instance of Ticket connector - ticketConnector = None +class TicketSharepointSyncInterface: + ticketConnector: TicketBase + task_sync_definition: dict + + # TODO: shareplum instance + + @classmethod + async def create( + cls, + ticket_connector: TicketBase, + ) -> "TicketSharepointSyncInterface": + instance = cls() + instance.ticketConnector = ticket_connector + return instance + + # TODO: 1. Read JIRA tickets + # TODO: 2. Transform tasks according to task_sync_definition (get_task_object) l. 79ff + + # TODO: 3. Create export file: Save transformed tasks to a timestamped export file in sharepoint + # - maybe not needed? + + # TODO: 4. Backup current main sync file + + # TODO: 5. Compare JIRA data (export file) with current main sync file and update line by line + # - update GET only + # - important so that we don't overwrite the changes from SELISE in the main sync file + + # TODO: 6. Take PUT changes from the main sync file and write it back to JIRA. + + # TODO: Write file to sharepoint folder + # TODO: Remove file from sharepoint folder + # TODO: Rename file in sharepoint folder + + # Next steps: + # - Complete connectorSharepoint CRUD-ish + # - pytest sharepoint connector + # - pytest JIRA connector + # - connect logic here... diff --git a/modules/routes/routeJira.py b/modules/routes/routeJira.py new file mode 100644 index 00000000..0a1b8195 --- /dev/null +++ b/modules/routes/routeJira.py @@ -0,0 +1,58 @@ +# Configure logger +import logging +from fastapi import APIRouter + +from modules.connectors.connectorTicketJira import ConnectorTicketJira + + +logger = logging.getLogger(__name__) + +router = APIRouter( + prefix="/api/users", + tags=["Manage Users"], +) + + +@router.post("/sync/delta-group") +async def sync_jira(): + logger.info("Syncing Jira issues...") + # Implement synchronization logic here + + jira_username = None + jira_api_token = None + sharepoint_client_id = None + sharepoint_client_secret = None + jira_url = "https://deltasecurity.atlassian.net" + project_code = "DCS" + issue_type = "Task" + task_sync_definition = { + # key=excel-header, [get:jira>excel | put: excel>jira, jira-xml-field-list] + "ID": ["get", ["key"]], + "Module Category": ["get", ["fields", "customfield_10058", "value"]], + "Summary": ["get", ["fields", "summary"]], + "Description": ["get", ["fields", "description"]], + "References": ["get", ["fields", "customfield_10066"]], + "Priority": ["get", ["fields", "priority", "name"]], + "Issue Status": ["get", ["fields", "customfield_10062"]], + "Assignee": ["get", ["fields", "assignee", "displayName"]], + "Issue Created": ["get", ["fields", "created"]], + "Due Date": ["get", ["fields", "duedate"]], + "DELTA Comments": ["get", ["fields", "customfield_10060"]], + "SELISE Ticket References": ["put", ["fields", "customfield_10067"]], + "SELISE Status Values": ["put", ["fields", "customfield_10065"]], + "SELISE Comments": ["put", ["fields", "customfield_10064"]], + } + + # Create the jira connector instance + jira_connector = ConnectorTicketJira( + jira_username=jira_username, + jira_api_token=jira_api_token, + jira_url=jira_url, + project_code=project_code, + issue_type=issue_type, + ) + + # Read the JIRA tickets + jira_attributes = await jira_connector.read_tasks(limit=0) + + return {"message": "Jira issues synchronized successfully"} diff --git a/requirements.txt b/requirements.txt index 783db728..75f2d078 100644 --- a/requirements.txt +++ b/requirements.txt @@ -43,6 +43,7 @@ chardet>=5.0.0 # Für Zeichensatzerkennung bei Webinhalten aiohttp>=3.8.0 # Required for SharePoint operations (async HTTP) selenium>=4.15.0 # Required for web automation and JavaScript-heavy pages tavily-python==0.7.11 # Tavily SDK +Office365-REST-Python-Client==2.6.2 # Easy Sharepoint integration ## Image Processing Pillow>=10.0.0 # Für Bildverarbeitung (als PIL importiert)