773 lines
25 KiB
Python
773 lines
25 KiB
Python
"""
|
|
Integration app models for external system integrations.
|
|
"""
|
|
|
|
import uuid
|
|
import json
|
|
from datetime import datetime, timedelta
|
|
from decimal import Decimal
|
|
from django.db import models
|
|
from django.contrib.auth import get_user_model
|
|
from django.core.validators import MinValueValidator, MaxValueValidator
|
|
from django.utils import timezone
|
|
from core.models import Tenant
|
|
from django.conf import settings
|
|
|
|
|
|
|
|
class ExternalSystem(models.Model):
|
|
"""
|
|
External system configuration for integrations.
|
|
"""
|
|
|
|
class SystemType(models.TextChoices):
|
|
EHR = 'EHR', 'Electronic Health Record'
|
|
HIS = 'HIS', 'Hospital Information System'
|
|
LIS = 'LIS', 'Laboratory Information System'
|
|
RIS = 'RIS', 'Radiology Information System'
|
|
PACS = 'PACS', 'Picture Archiving System'
|
|
PHARMACY = 'PHARMACY', 'Pharmacy Management System'
|
|
BILLING = 'BILLING', 'Billing System'
|
|
INSURANCE = 'INSURANCE', 'Insurance System'
|
|
GOVERNMENT = 'GOVERNMENT', 'Government System'
|
|
VENDOR = 'VENDOR', 'Vendor System'
|
|
API = 'API', 'API Service'
|
|
DATABASE = 'DATABASE', 'Database System'
|
|
FILE = 'FILE', 'File System'
|
|
FTP = 'FTP', 'FTP Server'
|
|
SFTP = 'SFTP', 'SFTP Server'
|
|
CLOUD = 'CLOUD', 'Cloud Service'
|
|
IOT = 'IOT', 'IoT Device'
|
|
MONITORING = 'MONITORING', 'Monitoring System'
|
|
ANALYTICS = 'ANALYTICS', 'Analytics Platform'
|
|
OTHER = 'OTHER', 'Other System'
|
|
|
|
class AuthenticationType(models.TextChoices):
|
|
NONE = 'NONE', 'No Authentication'
|
|
BASIC = 'BASIC', 'Basic Authentication'
|
|
BEARER = 'BEARER', 'Bearer Token'
|
|
API_KEY = 'API_KEY', 'API Key'
|
|
OAUTH2 = 'OAUTH2', 'OAuth 2.0'
|
|
CERTIFICATE = 'CERTIFICATE', 'Client Certificate'
|
|
CUSTOM = 'CUSTOM', 'Custom Authentication'
|
|
|
|
system_id = models.UUIDField(
|
|
primary_key=True,
|
|
default=uuid.uuid4,
|
|
editable=False
|
|
)
|
|
tenant = models.ForeignKey(
|
|
Tenant,
|
|
on_delete=models.CASCADE,
|
|
related_name='external_systems'
|
|
)
|
|
name = models.CharField(max_length=200)
|
|
description = models.TextField(blank=True)
|
|
system_type = models.CharField(
|
|
max_length=20,
|
|
choices=SystemType.choices,
|
|
)
|
|
vendor = models.CharField(max_length=200, blank=True)
|
|
version = models.CharField(max_length=100, blank=True)
|
|
|
|
# Connection details
|
|
base_url = models.URLField(blank=True)
|
|
host = models.CharField(max_length=255, blank=True)
|
|
port = models.PositiveIntegerField(
|
|
null=True,
|
|
blank=True,
|
|
validators=[MinValueValidator(1), MaxValueValidator(65535)]
|
|
)
|
|
database_name = models.CharField(max_length=200, blank=True)
|
|
|
|
# Authentication
|
|
authentication_type = models.CharField(
|
|
max_length=20,
|
|
choices=AuthenticationType.choices,
|
|
default='none'
|
|
)
|
|
authentication_config = models.JSONField(default=dict, blank=True)
|
|
|
|
# Configuration
|
|
configuration = models.JSONField(default=dict, blank=True)
|
|
timeout_seconds = models.PositiveIntegerField(default=30)
|
|
retry_attempts = models.PositiveIntegerField(default=3)
|
|
retry_delay_seconds = models.PositiveIntegerField(default=5)
|
|
|
|
# Status
|
|
is_active = models.BooleanField(default=True)
|
|
is_healthy = models.BooleanField(default=False)
|
|
last_health_check = models.DateTimeField(null=True, blank=True)
|
|
health_check_interval = models.PositiveIntegerField(
|
|
default=300, # 5 minutes
|
|
help_text="Health check interval in seconds"
|
|
)
|
|
|
|
# Usage tracking
|
|
connection_count = models.PositiveIntegerField(default=0)
|
|
success_count = models.PositiveIntegerField(default=0)
|
|
failure_count = models.PositiveIntegerField(default=0)
|
|
last_used_at = models.DateTimeField(null=True, blank=True)
|
|
|
|
# Metadata
|
|
created_at = models.DateTimeField(auto_now_add=True)
|
|
updated_at = models.DateTimeField(auto_now=True)
|
|
created_by = models.ForeignKey(
|
|
settings.AUTH_USER_MODEL,
|
|
on_delete=models.SET_NULL,
|
|
null=True,
|
|
blank=True,
|
|
related_name='created_external_systems'
|
|
)
|
|
|
|
class Meta:
|
|
db_table = 'integration_external_system'
|
|
indexes = [
|
|
models.Index(fields=['tenant', 'system_type']),
|
|
models.Index(fields=['tenant', 'is_active']),
|
|
models.Index(fields=['tenant', 'is_healthy']),
|
|
models.Index(fields=['last_health_check']),
|
|
]
|
|
unique_together = [['tenant', 'name']]
|
|
|
|
def __str__(self):
|
|
return f"{self.name} ({self.get_system_type_display()})"
|
|
|
|
@property
|
|
def success_rate(self):
|
|
"""Calculate success rate percentage."""
|
|
total = self.connection_count
|
|
if total == 0:
|
|
return 0.0
|
|
return (self.success_count / total) * 100
|
|
|
|
@property
|
|
def is_due_for_health_check(self):
|
|
"""Check if system is due for health check."""
|
|
if not self.last_health_check:
|
|
return True
|
|
next_check = self.last_health_check + timedelta(seconds=self.health_check_interval)
|
|
return timezone.now() >= next_check
|
|
|
|
|
|
class IntegrationEndpoint(models.Model):
|
|
"""
|
|
Integration endpoint configuration.
|
|
"""
|
|
|
|
class EndpointType(models.TextChoices):
|
|
REST_API = 'REST_API', 'REST API'
|
|
SOAP = 'SOAP', 'SOAP Web Service'
|
|
HL7 = 'HL7', 'HL7 Interface'
|
|
DICOM = 'DICOM', 'DICOM Service'
|
|
FTP = 'FTP', 'FTP Transfer'
|
|
SFTP = 'SFTP', 'SFTP Transfer'
|
|
DATABASE = 'DATABASE', 'Database Query'
|
|
FILE = 'FILE', 'File Processing'
|
|
WEBHOOK = 'WEBHOOK', 'Webhook'
|
|
QUEUE = 'QUEUE', 'Message Queue'
|
|
EMAIL = 'EMAIL', 'Email'
|
|
CUSTOM = 'CUSTOM', 'Custom Integration'
|
|
|
|
class HttpMethod(models.TextChoices):
|
|
GET = 'GET', 'GET'
|
|
POST = 'POST', 'POST'
|
|
PUT = 'PUT', 'PUT'
|
|
PATCH = 'PATCH', 'PATCH'
|
|
DELETE = 'DELETE', 'DELETE'
|
|
HEAD = 'HEAD', 'HEAD'
|
|
OPTIONS = 'OPTIONS', 'OPTIONS'
|
|
|
|
class Direction(models.TextChoices):
|
|
INBOUND = 'INBOUND', 'Inbound'
|
|
OUTBOUND = 'OUTBOUND', 'Outbound'
|
|
BIDIRECTIONAL = 'BIDIRECTIONAL', 'Bidirectional'
|
|
|
|
endpoint_id = models.UUIDField(
|
|
primary_key=True,
|
|
default=uuid.uuid4,
|
|
editable=False
|
|
)
|
|
external_system = models.ForeignKey(
|
|
ExternalSystem,
|
|
on_delete=models.CASCADE,
|
|
related_name='endpoints'
|
|
)
|
|
name = models.CharField(max_length=200)
|
|
description = models.TextField(blank=True)
|
|
endpoint_type = models.CharField(
|
|
max_length=20,
|
|
choices=EndpointType.choices,
|
|
)
|
|
|
|
# Endpoint details
|
|
path = models.CharField(max_length=500, blank=True)
|
|
method = models.CharField(
|
|
max_length=10,
|
|
choices=HttpMethod.choices,
|
|
default=HttpMethod.GET,
|
|
)
|
|
direction = models.CharField(
|
|
max_length=20,
|
|
choices=Direction.choices,
|
|
default=Direction.OUTBOUND,
|
|
)
|
|
|
|
# Configuration
|
|
headers = models.JSONField(default=dict, blank=True)
|
|
parameters = models.JSONField(default=dict, blank=True)
|
|
request_format = models.CharField(max_length=50, default='json')
|
|
response_format = models.CharField(max_length=50, default='json')
|
|
|
|
# Data mapping
|
|
request_mapping = models.JSONField(default=dict, blank=True)
|
|
response_mapping = models.JSONField(default=dict, blank=True)
|
|
|
|
# Validation
|
|
request_schema = models.JSONField(default=dict, blank=True)
|
|
response_schema = models.JSONField(default=dict, blank=True)
|
|
|
|
# Status
|
|
is_active = models.BooleanField(default=True)
|
|
|
|
# Usage tracking
|
|
execution_count = models.PositiveIntegerField(default=0)
|
|
success_count = models.PositiveIntegerField(default=0)
|
|
failure_count = models.PositiveIntegerField(default=0)
|
|
last_executed_at = models.DateTimeField(null=True, blank=True)
|
|
|
|
# Metadata
|
|
created_at = models.DateTimeField(auto_now_add=True)
|
|
updated_at = models.DateTimeField(auto_now=True)
|
|
created_by = models.ForeignKey(
|
|
settings.AUTH_USER_MODEL,
|
|
on_delete=models.SET_NULL,
|
|
null=True,
|
|
blank=True,
|
|
related_name='created_integration_endpoints'
|
|
)
|
|
|
|
class Meta:
|
|
db_table = 'integration_endpoint'
|
|
indexes = [
|
|
models.Index(fields=['external_system', 'endpoint_type']),
|
|
models.Index(fields=['external_system', 'is_active']),
|
|
models.Index(fields=['direction']),
|
|
models.Index(fields=['last_executed_at']),
|
|
]
|
|
unique_together = [['external_system', 'name']]
|
|
|
|
def __str__(self):
|
|
return f"{self.external_system.name} - {self.name}"
|
|
|
|
@property
|
|
def success_rate(self):
|
|
"""Calculate success rate percentage."""
|
|
total = self.execution_count
|
|
if total == 0:
|
|
return 0.0
|
|
return (self.success_count / total) * 100
|
|
|
|
|
|
class DataMapping(models.Model):
|
|
"""
|
|
Data mapping configuration for field transformations.
|
|
"""
|
|
|
|
class MappingType(models.TextChoices):
|
|
FIELD = 'field', 'Field Mapping'
|
|
VALUE = 'value', 'Value Mapping'
|
|
TRANSFORMATION = 'transformation', 'Data Transformation'
|
|
VALIDATION = 'validation', 'Data Validation'
|
|
ENRICHMENT = 'enrichment', 'Data Enrichment'
|
|
FILTERING = 'filtering', 'Data Filtering'
|
|
|
|
class TransformationType(models.TextChoices):
|
|
NONE = 'none', 'No Transformation'
|
|
FORMAT = 'format', 'Format Conversion'
|
|
CALCULATION = 'calculation', 'Calculation'
|
|
LOOKUP = 'lookup', 'Lookup Table'
|
|
CONCATENATION = 'concatenation', 'Concatenation'
|
|
SPLIT = 'split', 'Split Field'
|
|
DEFAULT = 'default', 'Default Value'
|
|
CONDITIONAL = 'conditional', 'Conditional Logic'
|
|
CUSTOM = 'custom', 'Custom Function'
|
|
|
|
mapping_id = models.UUIDField(
|
|
primary_key=True,
|
|
default=uuid.uuid4,
|
|
editable=False
|
|
)
|
|
endpoint = models.ForeignKey(
|
|
IntegrationEndpoint,
|
|
on_delete=models.CASCADE,
|
|
related_name='data_mappings'
|
|
)
|
|
name = models.CharField(max_length=200)
|
|
description = models.TextField(blank=True)
|
|
mapping_type = models.CharField(
|
|
max_length=20,
|
|
choices=MappingType.choices,
|
|
)
|
|
|
|
# Source configuration
|
|
source_field = models.CharField(max_length=500)
|
|
source_format = models.CharField(max_length=100, blank=True)
|
|
source_validation = models.JSONField(default=dict, blank=True)
|
|
|
|
# Target configuration
|
|
target_field = models.CharField(max_length=500)
|
|
target_format = models.CharField(max_length=100, blank=True)
|
|
target_validation = models.JSONField(default=dict, blank=True)
|
|
|
|
# Transformation
|
|
transformation_type = models.CharField(
|
|
max_length=20,
|
|
choices=TransformationType.choices,
|
|
default=TransformationType.NONE,
|
|
)
|
|
transformation_config = models.JSONField(default=dict, blank=True)
|
|
|
|
# Validation rules
|
|
is_required = models.BooleanField(default=False)
|
|
validation_rules = models.JSONField(default=dict, blank=True)
|
|
default_value = models.TextField(blank=True)
|
|
|
|
# Status
|
|
is_active = models.BooleanField(default=True)
|
|
|
|
# Usage tracking
|
|
usage_count = models.PositiveIntegerField(default=0)
|
|
success_count = models.PositiveIntegerField(default=0)
|
|
failure_count = models.PositiveIntegerField(default=0)
|
|
last_used_at = models.DateTimeField(null=True, blank=True)
|
|
|
|
# Metadata
|
|
created_at = models.DateTimeField(auto_now_add=True)
|
|
updated_at = models.DateTimeField(auto_now=True)
|
|
created_by = models.ForeignKey(
|
|
settings.AUTH_USER_MODEL,
|
|
on_delete=models.SET_NULL,
|
|
null=True,
|
|
blank=True,
|
|
related_name='created_data_mappings'
|
|
)
|
|
|
|
class Meta:
|
|
db_table = 'integration_data_mapping'
|
|
indexes = [
|
|
models.Index(fields=['endpoint', 'mapping_type']),
|
|
models.Index(fields=['endpoint', 'is_active']),
|
|
models.Index(fields=['source_field']),
|
|
models.Index(fields=['target_field']),
|
|
]
|
|
unique_together = [['endpoint', 'name']]
|
|
|
|
def __str__(self):
|
|
return f"{self.endpoint.name} - {self.name}"
|
|
|
|
@property
|
|
def success_rate(self):
|
|
"""Calculate success rate percentage."""
|
|
total = self.usage_count
|
|
if total == 0:
|
|
return 0.0
|
|
return (self.success_count / total) * 100
|
|
|
|
|
|
class IntegrationExecution(models.Model):
|
|
"""
|
|
Integration execution tracking and logging.
|
|
"""
|
|
|
|
class ExecutionType(models.TextChoices):
|
|
MANUAL = 'manual', 'Manual Execution'
|
|
SCHEDULED = 'scheduled', 'Scheduled Execution'
|
|
TRIGGERED = 'triggered', 'Event Triggered'
|
|
WEBHOOK = 'webhook', 'Webhook Triggered'
|
|
API = 'api', 'API Call'
|
|
BATCH = 'batch', 'Batch Processing'
|
|
REAL_TIME = 'real_time', 'Real-time Processing'
|
|
|
|
class ExecutionStatus(models.TextChoices):
|
|
PENDING = 'pending', 'Pending'
|
|
RUNNING = 'running', 'Running'
|
|
COMPLETED = 'completed', 'Completed'
|
|
FAILED = 'failed', 'Failed'
|
|
CANCELLED = 'cancelled', 'Cancelled'
|
|
TIMEOUT = 'timeout', 'Timeout'
|
|
RETRY = 'retry', 'Retrying'
|
|
|
|
execution_id = models.UUIDField(
|
|
primary_key=True,
|
|
default=uuid.uuid4,
|
|
editable=False
|
|
)
|
|
endpoint = models.ForeignKey(
|
|
IntegrationEndpoint,
|
|
on_delete=models.CASCADE,
|
|
related_name='executions'
|
|
)
|
|
execution_type = models.CharField(
|
|
max_length=20,
|
|
choices=ExecutionType.choices,
|
|
)
|
|
|
|
# Execution details
|
|
status = models.CharField(
|
|
max_length=20,
|
|
choices=ExecutionStatus.choices,
|
|
default=ExecutionStatus.PENDING,
|
|
)
|
|
started_at = models.DateTimeField(auto_now_add=True)
|
|
completed_at = models.DateTimeField(null=True, blank=True)
|
|
|
|
# Request/Response
|
|
request_data = models.JSONField(default=dict, blank=True)
|
|
response_data = models.JSONField(default=dict, blank=True)
|
|
request_size_bytes = models.PositiveIntegerField(default=0)
|
|
response_size_bytes = models.PositiveIntegerField(default=0)
|
|
|
|
# Performance metrics
|
|
processing_time_ms = models.PositiveIntegerField(null=True, blank=True)
|
|
network_time_ms = models.PositiveIntegerField(null=True, blank=True)
|
|
|
|
# Error handling
|
|
error_message = models.TextField(blank=True)
|
|
error_details = models.JSONField(default=dict, blank=True)
|
|
retry_count = models.PositiveIntegerField(default=0)
|
|
|
|
# External tracking
|
|
external_id = models.CharField(max_length=200, blank=True)
|
|
correlation_id = models.CharField(max_length=200, blank=True)
|
|
|
|
# Metadata
|
|
triggered_by = models.ForeignKey(
|
|
settings.AUTH_USER_MODEL,
|
|
on_delete=models.SET_NULL,
|
|
null=True,
|
|
blank=True,
|
|
related_name='triggered_integrations'
|
|
)
|
|
metadata = models.JSONField(default=dict, blank=True)
|
|
|
|
class Meta:
|
|
db_table = 'integration_execution'
|
|
indexes = [
|
|
models.Index(fields=['endpoint', 'status']),
|
|
models.Index(fields=['endpoint', 'started_at']),
|
|
models.Index(fields=['execution_type']),
|
|
models.Index(fields=['correlation_id']),
|
|
models.Index(fields=['external_id']),
|
|
]
|
|
|
|
def __str__(self):
|
|
return f"{self.endpoint.name} - {self.execution_id}"
|
|
|
|
@property
|
|
def duration(self):
|
|
"""Calculate execution duration."""
|
|
if self.completed_at and self.started_at:
|
|
return self.completed_at - self.started_at
|
|
return None
|
|
|
|
@property
|
|
def is_successful(self):
|
|
"""Check if execution was successful."""
|
|
return self.status == 'completed'
|
|
|
|
|
|
class WebhookEndpoint(models.Model):
|
|
"""
|
|
Webhook endpoint configuration for receiving external data.
|
|
"""
|
|
|
|
class AuthenticationType(models.TextChoices):
|
|
NONE = 'none', 'No Authentication'
|
|
BASIC = 'basic', 'Basic Authentication'
|
|
BEARER = 'bearer', 'Bearer Token'
|
|
API_KEY = 'api_key', 'API Key'
|
|
SIGNATURE = 'signature', 'Signature Verification'
|
|
IP_WHITELIST = 'ip_whitelist', 'IP Whitelist'
|
|
CUSTOM = 'custom', 'Custom Authentication'
|
|
|
|
webhook_id = models.UUIDField(
|
|
primary_key=True,
|
|
default=uuid.uuid4,
|
|
editable=False
|
|
)
|
|
external_system = models.ForeignKey(
|
|
ExternalSystem,
|
|
on_delete=models.CASCADE,
|
|
related_name='webhooks'
|
|
)
|
|
name = models.CharField(max_length=200)
|
|
description = models.TextField(blank=True)
|
|
|
|
# Webhook configuration
|
|
url_path = models.CharField(
|
|
max_length=500,
|
|
unique=True,
|
|
help_text="URL path for webhook endpoint"
|
|
)
|
|
allowed_methods = models.JSONField(
|
|
default=list,
|
|
help_text="Allowed HTTP methods"
|
|
)
|
|
|
|
# Authentication
|
|
authentication_type = models.CharField(
|
|
max_length=20,
|
|
choices=AuthenticationType.choices,
|
|
default=AuthenticationType.NONE,
|
|
)
|
|
authentication_config = models.JSONField(default=dict, blank=True)
|
|
|
|
# Processing configuration
|
|
data_mapping = models.ForeignKey(
|
|
DataMapping,
|
|
on_delete=models.SET_NULL,
|
|
null=True,
|
|
blank=True,
|
|
related_name='webhooks'
|
|
)
|
|
processing_config = models.JSONField(default=dict, blank=True)
|
|
|
|
# Rate limiting
|
|
rate_limit_per_minute = models.PositiveIntegerField(
|
|
null=True,
|
|
blank=True,
|
|
help_text="Maximum requests per minute"
|
|
)
|
|
rate_limit_per_hour = models.PositiveIntegerField(
|
|
null=True,
|
|
blank=True,
|
|
help_text="Maximum requests per hour"
|
|
)
|
|
|
|
# Status
|
|
is_active = models.BooleanField(default=True)
|
|
|
|
# Usage tracking
|
|
request_count = models.PositiveIntegerField(default=0)
|
|
success_count = models.PositiveIntegerField(default=0)
|
|
failure_count = models.PositiveIntegerField(default=0)
|
|
last_request_at = models.DateTimeField(null=True, blank=True)
|
|
|
|
# Metadata
|
|
created_at = models.DateTimeField(auto_now_add=True)
|
|
updated_at = models.DateTimeField(auto_now=True)
|
|
created_by = models.ForeignKey(
|
|
settings.AUTH_USER_MODEL,
|
|
on_delete=models.SET_NULL,
|
|
null=True,
|
|
blank=True,
|
|
related_name='created_webhooks'
|
|
)
|
|
|
|
class Meta:
|
|
db_table = 'integration_webhook_endpoint'
|
|
indexes = [
|
|
models.Index(fields=['external_system', 'is_active']),
|
|
models.Index(fields=['url_path']),
|
|
models.Index(fields=['last_request_at']),
|
|
]
|
|
unique_together = [['external_system', 'name']]
|
|
|
|
def __str__(self):
|
|
return f"{self.external_system.name} - {self.name}"
|
|
|
|
@property
|
|
def success_rate(self):
|
|
"""Calculate success rate percentage."""
|
|
total = self.request_count
|
|
if total == 0:
|
|
return 0.0
|
|
return (self.success_count / total) * 100
|
|
|
|
@property
|
|
def full_url(self):
|
|
"""Get full webhook URL."""
|
|
# This would be constructed based on the application's base URL
|
|
return f"/api/webhooks/{self.url_path}"
|
|
|
|
|
|
class WebhookExecution(models.Model):
|
|
"""
|
|
Webhook execution tracking and logging.
|
|
"""
|
|
|
|
class IntegrationStatus(models.TextChoices):
|
|
RECEIVED = 'received', 'Received'
|
|
PROCESSING = 'processing', 'Processing'
|
|
COMPLETED = 'completed', 'Completed'
|
|
FAILED = 'failed', 'Failed'
|
|
REJECTED = 'rejected', 'Rejected'
|
|
TIMEOUT = 'timeout', 'Timeout'
|
|
|
|
execution_id = models.UUIDField(
|
|
primary_key=True,
|
|
default=uuid.uuid4,
|
|
editable=False
|
|
)
|
|
webhook = models.ForeignKey(
|
|
WebhookEndpoint,
|
|
on_delete=models.CASCADE,
|
|
related_name='executions'
|
|
)
|
|
|
|
# Request details
|
|
method = models.CharField(max_length=10)
|
|
headers = models.JSONField(default=dict, blank=True)
|
|
query_params = models.JSONField(default=dict, blank=True)
|
|
payload = models.JSONField(default=dict, blank=True)
|
|
payload_size_bytes = models.PositiveIntegerField(default=0)
|
|
|
|
# Client information
|
|
client_ip = models.GenericIPAddressField(null=True, blank=True)
|
|
user_agent = models.TextField(blank=True)
|
|
|
|
# Processing
|
|
status = models.CharField(
|
|
max_length=20,
|
|
choices=IntegrationStatus.choices,
|
|
default=IntegrationStatus.RECEIVED,
|
|
)
|
|
received_at = models.DateTimeField(auto_now_add=True)
|
|
processed_at = models.DateTimeField(null=True, blank=True)
|
|
processing_time_ms = models.PositiveIntegerField(null=True, blank=True)
|
|
|
|
# Response
|
|
response_status = models.PositiveIntegerField(default=200)
|
|
response_data = models.JSONField(default=dict, blank=True)
|
|
|
|
# Error handling
|
|
error_message = models.TextField(blank=True)
|
|
error_details = models.JSONField(default=dict, blank=True)
|
|
|
|
# External tracking
|
|
external_id = models.CharField(max_length=200, blank=True)
|
|
correlation_id = models.CharField(max_length=200, blank=True)
|
|
|
|
# Metadata
|
|
metadata = models.JSONField(default=dict, blank=True)
|
|
|
|
class Meta:
|
|
db_table = 'integration_webhook_execution'
|
|
indexes = [
|
|
models.Index(fields=['webhook', 'status']),
|
|
models.Index(fields=['webhook', 'received_at']),
|
|
models.Index(fields=['client_ip']),
|
|
models.Index(fields=['correlation_id']),
|
|
models.Index(fields=['external_id']),
|
|
]
|
|
|
|
def __str__(self):
|
|
return f"{self.webhook.name} - {self.execution_id}"
|
|
|
|
@property
|
|
def duration(self):
|
|
"""Calculate processing duration."""
|
|
if self.processed_at and self.received_at:
|
|
return self.processed_at - self.received_at
|
|
return None
|
|
|
|
@property
|
|
def is_successful(self):
|
|
"""Check if execution was successful."""
|
|
return self.status == 'completed'
|
|
|
|
|
|
class IntegrationLog(models.Model):
|
|
"""
|
|
Integration activity logging for audit and monitoring.
|
|
"""
|
|
|
|
class LogLevel(models.TextChoices):
|
|
DEBUG = 'debug', 'Debug'
|
|
INFO = 'info', 'Info'
|
|
WARNING = 'warning', 'Warning'
|
|
ERROR = 'error', 'Error'
|
|
CRITICAL = 'critical', 'Critical'
|
|
|
|
class LogCategory(models.TextChoices):
|
|
CONNECTION = 'connection', 'Connection'
|
|
AUTHENTICATION = 'authentication', 'Authentication'
|
|
DATA_TRANSFER = 'data_transfer', 'Data Transfer'
|
|
TRANSFORMATION = 'transformation', 'Data Transformation'
|
|
VALIDATION = 'validation', 'Data Validation'
|
|
ERROR = 'error', 'Error'
|
|
PERFORMANCE = 'performance', 'Performance'
|
|
SECURITY = 'security', 'Security'
|
|
AUDIT = 'audit', 'Audit'
|
|
SYSTEM = 'system', 'System'
|
|
|
|
log_id = models.UUIDField(
|
|
primary_key=True,
|
|
default=uuid.uuid4,
|
|
editable=False
|
|
)
|
|
external_system = models.ForeignKey(
|
|
ExternalSystem,
|
|
on_delete=models.CASCADE,
|
|
related_name='logs',
|
|
null=True,
|
|
blank=True
|
|
)
|
|
endpoint = models.ForeignKey(
|
|
IntegrationEndpoint,
|
|
on_delete=models.CASCADE,
|
|
related_name='logs',
|
|
null=True,
|
|
blank=True
|
|
)
|
|
execution = models.ForeignKey(
|
|
IntegrationExecution,
|
|
on_delete=models.CASCADE,
|
|
related_name='logs',
|
|
null=True,
|
|
blank=True
|
|
)
|
|
|
|
# Log details
|
|
level = models.CharField(
|
|
max_length=20,
|
|
choices=LogLevel.choices,
|
|
)
|
|
category = models.CharField(
|
|
max_length=20,
|
|
choices=LogCategory.choices,
|
|
)
|
|
message = models.TextField()
|
|
details = models.JSONField(default=dict, blank=True)
|
|
|
|
# Context
|
|
correlation_id = models.CharField(max_length=200, blank=True)
|
|
user = models.ForeignKey(
|
|
settings.AUTH_USER_MODEL,
|
|
on_delete=models.SET_NULL,
|
|
null=True,
|
|
blank=True,
|
|
related_name='integration_logs'
|
|
)
|
|
|
|
# Metadata
|
|
timestamp = models.DateTimeField(auto_now_add=True)
|
|
metadata = models.JSONField(default=dict, blank=True)
|
|
|
|
class Meta:
|
|
db_table = 'integration_log'
|
|
indexes = [
|
|
models.Index(fields=['external_system', 'level']),
|
|
models.Index(fields=['endpoint', 'level']),
|
|
models.Index(fields=['execution']),
|
|
models.Index(fields=['level', 'timestamp']),
|
|
models.Index(fields=['category', 'timestamp']),
|
|
models.Index(fields=['correlation_id']),
|
|
]
|
|
|
|
def __str__(self):
|
|
system_name = self.external_system.name if self.external_system else "System"
|
|
return f"{system_name} - {self.get_level_display()}: {self.message[:50]}"
|
|
|