Implement content synchronization from WordPress and Shopify in ContentSyncService

- Added methods to sync content from WordPress and Shopify to IGNY8, including error handling and logging.
- Introduced internal methods for fetching posts from WordPress and products from Shopify.
- Updated IntegrationService to include a method for retrieving active integrations for a site.
- Enhanced test cases to cover new functionality and ensure proper setup of test data, including industry and sector associations.
This commit is contained in:
IGNY8 VPS (Salman)
2025-11-18 02:07:22 +00:00
parent 873f97ea3f
commit 51c3986e01
12 changed files with 583 additions and 64 deletions

View File

@@ -7,16 +7,18 @@ Adapter for deploying sites to IGNY8 Sites renderer.
import logging
import json
import os
from typing import Dict, Any
from typing import Dict, Any, Optional
from pathlib import Path
from datetime import datetime
from igny8_core.business.site_building.models import SiteBlueprint
from igny8_core.business.publishing.models import DeploymentRecord
from igny8_core.business.publishing.services.adapters.base_adapter import BaseAdapter
logger = logging.getLogger(__name__)
class SitesRendererAdapter:
class SitesRendererAdapter(BaseAdapter):
"""
Adapter for deploying sites to IGNY8 Sites renderer.
Writes site definitions to filesystem for Sites container to serve.
@@ -200,4 +202,113 @@ class SitesRendererAdapter:
# For now, return placeholder
site_id = site_blueprint.site.id
return f"https://{site_id}.igny8.com" # Placeholder
# BaseAdapter interface implementation
def publish(
self,
content: Any,
destination_config: Dict[str, Any]
) -> Dict[str, Any]:
"""
Publish content to destination (implements BaseAdapter interface).
Args:
content: SiteBlueprint to publish
destination_config: Destination-specific configuration
Returns:
dict: Publishing result
"""
if not isinstance(content, SiteBlueprint):
return {
'success': False,
'error': 'SitesRendererAdapter only accepts SiteBlueprint instances'
}
result = self.deploy(content)
if result.get('success'):
return {
'success': True,
'external_id': str(result.get('deployment_id')),
'url': result.get('deployment_url'),
'published_at': datetime.now(),
'metadata': {
'deployment_path': result.get('deployment_path'),
'version': result.get('version')
}
}
else:
return {
'success': False,
'error': result.get('error'),
'metadata': {}
}
def test_connection(
self,
config: Dict[str, Any]
) -> Dict[str, Any]:
"""
Test connection to Sites renderer (implements BaseAdapter interface).
Args:
config: Destination configuration
Returns:
dict: Connection test result
"""
sites_data_path = config.get('sites_data_path', os.getenv('SITES_DATA_PATH', '/data/app/sites-data'))
try:
path = Path(sites_data_path)
if path.exists() and path.is_dir():
return {
'success': True,
'message': 'Sites data directory is accessible',
'details': {'path': str(path)}
}
else:
return {
'success': False,
'message': f'Sites data directory does not exist: {sites_data_path}',
'details': {}
}
except Exception as e:
return {
'success': False,
'message': f'Error accessing sites data directory: {str(e)}',
'details': {}
}
def get_status(
self,
published_id: str,
config: Dict[str, Any]
) -> Dict[str, Any]:
"""
Get publishing status for published content (implements BaseAdapter interface).
Args:
published_id: Deployment record ID
config: Destination configuration
Returns:
dict: Status information
"""
try:
deployment = DeploymentRecord.objects.get(id=published_id)
return {
'status': deployment.status,
'url': deployment.deployment_url,
'updated_at': deployment.updated_at,
'metadata': deployment.metadata or {}
}
except DeploymentRecord.DoesNotExist:
return {
'status': 'not_found',
'url': None,
'updated_at': None,
'metadata': {}
}

View File

@@ -53,14 +53,15 @@ class WordPressAdapter(BaseAdapter):
client = self._get_client(destination_config)
# Extract content data
if hasattr(content, 'title') and hasattr(content, 'content'):
if hasattr(content, 'title'):
# Content model instance
title = content.title
content_html = content.content
# Try different possible attribute names for content
content_html = getattr(content, 'html_content', None) or getattr(content, 'content', None) or ''
elif isinstance(content, dict):
# Dict with content data
title = content.get('title', '')
content_html = content.get('content', '')
content_html = content.get('html_content') or content.get('content', '')
else:
raise ValueError(f"Unsupported content type: {type(content)}")
@@ -76,14 +77,19 @@ class WordPressAdapter(BaseAdapter):
featured_image_url=featured_image_url
)
if result.get('success'):
# Handle different response formats (for compatibility with mocks and real API)
if result.get('success') or result.get('id') or result.get('post_id'):
# Extract post ID from various possible fields
post_id = result.get('post_id') or result.get('id') or result.get('ID')
url = result.get('url') or result.get('link')
return {
'success': True,
'external_id': str(result.get('post_id')),
'url': result.get('url'),
'external_id': str(post_id) if post_id else None,
'url': url,
'published_at': datetime.now(),
'metadata': {
'post_id': result.get('post_id'),
'post_id': post_id,
'status': status
}
}
@@ -94,7 +100,7 @@ class WordPressAdapter(BaseAdapter):
'url': None,
'published_at': None,
'metadata': {
'error': result.get('error')
'error': result.get('error', 'Unknown error')
}
}