feat(preview-generation): implement thumbnail generation for documents; add preview upload to EspoCRM
This commit is contained in:
@@ -298,3 +298,117 @@ class EspoCRMAPI:
|
||||
|
||||
result = await self.list_entities(entity_type, where=where)
|
||||
return result.get('list', [])
|
||||
|
||||
async def upload_attachment(
|
||||
self,
|
||||
file_content: bytes,
|
||||
filename: str,
|
||||
parent_type: str,
|
||||
parent_id: str,
|
||||
field: str,
|
||||
mime_type: str = 'application/octet-stream',
|
||||
role: str = 'Attachment'
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
Upload an attachment to EspoCRM.
|
||||
|
||||
Args:
|
||||
file_content: File content as bytes
|
||||
filename: Name of the file
|
||||
parent_type: Parent entity type (e.g., 'Document')
|
||||
parent_id: Parent entity ID
|
||||
field: Field name for the attachment (e.g., 'preview')
|
||||
mime_type: MIME type of the file
|
||||
role: Attachment role (default: 'Attachment')
|
||||
|
||||
Returns:
|
||||
Attachment entity data
|
||||
"""
|
||||
self._log(f"Uploading attachment: {filename} ({len(file_content)} bytes) to {parent_type}/{parent_id}/{field}")
|
||||
|
||||
url = self.api_base_url.rstrip('/') + '/Attachment'
|
||||
headers = {
|
||||
'X-Api-Key': self.api_key,
|
||||
# Content-Type wird automatisch von aiohttp gesetzt für FormData
|
||||
}
|
||||
|
||||
# Erstelle FormData
|
||||
form_data = aiohttp.FormData()
|
||||
form_data.add_field('file', file_content, filename=filename, content_type=mime_type)
|
||||
form_data.add_field('parentType', parent_type)
|
||||
form_data.add_field('parentId', parent_id)
|
||||
form_data.add_field('field', field)
|
||||
form_data.add_field('role', role)
|
||||
form_data.add_field('name', filename)
|
||||
|
||||
effective_timeout = aiohttp.ClientTimeout(total=self.api_timeout_seconds)
|
||||
|
||||
async with aiohttp.ClientSession(timeout=effective_timeout) as session:
|
||||
try:
|
||||
async with session.post(url, headers=headers, data=form_data) as response:
|
||||
self._log(f"Upload response status: {response.status}", level='debug')
|
||||
|
||||
if response.status == 401:
|
||||
raise EspoCRMAuthError("Authentication failed - check API key")
|
||||
elif response.status == 403:
|
||||
raise EspoCRMError("Access forbidden")
|
||||
elif response.status == 404:
|
||||
raise EspoCRMError(f"Attachment endpoint not found")
|
||||
elif response.status >= 400:
|
||||
error_text = await response.text()
|
||||
raise EspoCRMError(f"Upload error {response.status}: {error_text}")
|
||||
|
||||
# Parse response
|
||||
if response.content_type == 'application/json':
|
||||
result = await response.json()
|
||||
attachment_id = result.get('id')
|
||||
self._log(f"✅ Attachment uploaded successfully: {attachment_id}")
|
||||
return result
|
||||
else:
|
||||
response_text = await response.text()
|
||||
self._log(f"⚠️ Non-JSON response: {response_text[:200]}", level='warn')
|
||||
return {'success': True, 'response': response_text}
|
||||
|
||||
except aiohttp.ClientError as e:
|
||||
self._log(f"Upload failed: {e}", level='error')
|
||||
raise EspoCRMError(f"Upload request failed: {e}") from e
|
||||
|
||||
async def download_attachment(self, attachment_id: str) -> bytes:
|
||||
"""
|
||||
Download an attachment from EspoCRM.
|
||||
|
||||
Args:
|
||||
attachment_id: Attachment ID
|
||||
|
||||
Returns:
|
||||
File content as bytes
|
||||
"""
|
||||
self._log(f"Downloading attachment: {attachment_id}")
|
||||
|
||||
url = self.api_base_url.rstrip('/') + f'/Attachment/file/{attachment_id}'
|
||||
headers = {
|
||||
'X-Api-Key': self.api_key,
|
||||
}
|
||||
|
||||
effective_timeout = aiohttp.ClientTimeout(total=self.api_timeout_seconds)
|
||||
|
||||
async with aiohttp.ClientSession(timeout=effective_timeout) as session:
|
||||
try:
|
||||
async with session.get(url, headers=headers) as response:
|
||||
if response.status == 401:
|
||||
raise EspoCRMAuthError("Authentication failed - check API key")
|
||||
elif response.status == 403:
|
||||
raise EspoCRMError("Access forbidden")
|
||||
elif response.status == 404:
|
||||
raise EspoCRMError(f"Attachment not found: {attachment_id}")
|
||||
elif response.status >= 400:
|
||||
error_text = await response.text()
|
||||
raise EspoCRMError(f"Download error {response.status}: {error_text}")
|
||||
|
||||
content = await response.read()
|
||||
self._log(f"✅ Downloaded {len(content)} bytes")
|
||||
return content
|
||||
|
||||
except aiohttp.ClientError as e:
|
||||
self._log(f"Download failed: {e}", level='error')
|
||||
raise EspoCRMError(f"Download request failed: {e}") from e
|
||||
|
||||
Reference in New Issue
Block a user