102 lines
4.8 KiB
Python
102 lines
4.8 KiB
Python
# Copyright (c) 2025 Patrick Motsch
|
|
# All rights reserved.
|
|
|
|
"""
|
|
API Client helper for SharePoint operations.
|
|
Handles Microsoft Graph API calls with timeout and error handling.
|
|
"""
|
|
|
|
import logging
|
|
import aiohttp
|
|
import asyncio
|
|
from typing import Dict, Any
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
class ApiClientHelper:
|
|
"""Helper for Microsoft Graph API calls"""
|
|
|
|
def __init__(self, methodInstance):
|
|
"""
|
|
Initialize API client helper.
|
|
|
|
Args:
|
|
methodInstance: Instance of MethodSharepoint (for access to services)
|
|
"""
|
|
self.method = methodInstance
|
|
self.services = methodInstance.services
|
|
|
|
async def makeGraphApiCall(self, endpoint: str, method: str = "GET", data: bytes = None) -> Dict[str, Any]:
|
|
"""
|
|
Make a Microsoft Graph API call with timeout and detailed logging.
|
|
|
|
Args:
|
|
endpoint: API endpoint (without base URL)
|
|
method: HTTP method (GET, POST, PUT)
|
|
data: Optional request body data (bytes)
|
|
|
|
Returns:
|
|
Dict with API response or error information
|
|
"""
|
|
try:
|
|
if not hasattr(self.services, 'sharepoint') or not self.services.sharepoint._target.accessToken:
|
|
return {"error": "SharePoint service not configured with access token"}
|
|
|
|
headers = {
|
|
"Authorization": f"Bearer {self.services.sharepoint._target.accessToken}",
|
|
"Content-Type": "application/json" if data and method != "PUT" else "application/octet-stream" if data else "application/json"
|
|
}
|
|
|
|
url = f"https://graph.microsoft.com/v1.0/{endpoint}"
|
|
logger.info(f"Making Graph API call: {method} {url}")
|
|
|
|
# Set timeout to 30 seconds
|
|
timeout = aiohttp.ClientTimeout(total=30)
|
|
|
|
async with aiohttp.ClientSession(timeout=timeout) as session:
|
|
if method == "GET":
|
|
logger.debug(f"Starting GET request to {url}")
|
|
async with session.get(url, headers=headers) as response:
|
|
logger.info(f"Graph API response: {response.status}")
|
|
if response.status == 200:
|
|
result = await response.json()
|
|
logger.debug(f"Graph API success: {len(str(result))} characters response")
|
|
return result
|
|
else:
|
|
errorText = await response.text()
|
|
logger.error(f"Graph API call failed: {response.status} - {errorText}")
|
|
return {"error": f"API call failed: {response.status} - {errorText}"}
|
|
|
|
elif method == "PUT":
|
|
logger.debug(f"Starting PUT request to {url}")
|
|
async with session.put(url, headers=headers, data=data) as response:
|
|
logger.info(f"Graph API response: {response.status}")
|
|
if response.status in [200, 201]:
|
|
result = await response.json()
|
|
logger.debug(f"Graph API success: {len(str(result))} characters response")
|
|
return result
|
|
else:
|
|
errorText = await response.text()
|
|
logger.error(f"Graph API call failed: {response.status} - {errorText}")
|
|
return {"error": f"API call failed: {response.status} - {errorText}"}
|
|
|
|
elif method == "POST":
|
|
logger.debug(f"Starting POST request to {url}")
|
|
async with session.post(url, headers=headers, data=data) as response:
|
|
logger.info(f"Graph API response: {response.status}")
|
|
if response.status in [200, 201]:
|
|
result = await response.json()
|
|
logger.debug(f"Graph API success: {len(str(result))} characters response")
|
|
return result
|
|
else:
|
|
errorText = await response.text()
|
|
logger.error(f"Graph API call failed: {response.status} - {errorText}")
|
|
return {"error": f"API call failed: {response.status} - {errorText}"}
|
|
|
|
except asyncio.TimeoutError:
|
|
logger.error(f"Graph API call timed out after 30 seconds: {endpoint}")
|
|
return {"error": f"API call timed out after 30 seconds: {endpoint}"}
|
|
except Exception as e:
|
|
logger.error(f"Error making Graph API call: {str(e)}")
|
|
return {"error": f"Error making Graph API call: {str(e)}"}
|
|
|