feat(templates): add duplicate creation prevention

This commit is contained in:
YuehuCao 2025-08-12 13:58:08 +08:00
parent f5206175da
commit 58c77ce002
4 changed files with 108 additions and 39 deletions

View File

@ -44,10 +44,10 @@ class TemplateMessageManager:
)
result = await self.template_message_service.create_global_template(template)
action = "skipped" if hasattr(result, "_is_existing") and result._is_existing else "created"
await self.module_logger.log_info(
info="Global template created",
properties={"template_id": template_id, "region": region}
info=f"Global template {action}",
properties={"template_id": template_id, "region": region, "action": action}
)
return result
@ -157,12 +157,14 @@ class TemplateMessageManager:
result = await self.template_message_service.create_tenant_template(tenant_id, template)
action = "skipped" if hasattr(result, "_is_existing") and result._is_existing else "created"
await self.module_logger.log_info(
info="Tenant template created",
info=f"Tenant template {action}",
properties={
"tenant_id": tenant_id,
"template_id": template_id,
"region": region
"region": region,
"action": action
}
)

View File

@ -42,21 +42,47 @@ class TemplateMessageHandler:
# ==================== global templates ====================
async def create_global_template(self, template: MessageTemplateDoc) -> MessageTemplateDoc:
"""create global template"""
"""create global template with upsert support"""
try:
await template.create()
await self.module_logger.log_info(
info="Template created in database",
properties={
"template_id": template.template_id,
"tenant_id": template.tenant_id,
"region": template.region
}
)
return template
# check if template already exists
existing_template = await MessageTemplateDoc.find_one({
"template_id": template.template_id,
"tenant_id": template.tenant_id,
"region": template.region
})
if existing_template:
# if template already exists, skip creation
await self.module_logger.log_info(
info="Global template already exists, skipping creation",
properties={
"template_id": template.template_id,
"tenant_id": template.tenant_id,
"region": template.region
}
)
# mark as existing template
existing_template._is_existing = True
return existing_template
else:
# if template does not exist, create new template
await template.create()
await self.module_logger.log_info(
info="Template created in database",
properties={
"template_id": template.template_id,
"tenant_id": template.tenant_id,
"region": template.region
}
)
# mark as new template
template._is_existing = False
return template
except Exception as e:
await self.module_logger.log_error(
error="Failed to create template in database",
error="Failed to create/update template in database",
properties={
"template_id": template.template_id,
"error": str(e)
@ -258,23 +284,50 @@ class TemplateMessageHandler:
raise
async def create_tenant_template(self, tenant_id: str, template: MessageTemplateDoc) -> MessageTemplateDoc:
"""create tenant template"""
"""create tenant template with upsert support"""
try:
await template.create()
await self.module_logger.log_info(
info="Template created in database",
# check if template already exists
existing_template = await MessageTemplateDoc.find_one({
"template_id": template.template_id,
"tenant_id": template.tenant_id,
"region": template.region
})
if existing_template:
# if template already exists, skip creation
await self.module_logger.log_info(
info="Tenant template already exists, skipping creation",
properties={
"template_id": template.template_id,
"tenant_id": template.tenant_id,
"region": template.region
}
)
# mark as existing template
existing_template._is_existing = True
return existing_template
else:
# if template does not exist, create new template
await template.create()
await self.module_logger.log_info(
info="Template created in database",
properties={
"template_id": template.template_id,
"tenant_id": template.tenant_id,
"region": template.region
}
)
# mark as new template
template._is_existing = False
return template
except Exception as e:
await self.module_logger.log_error(
error="Failed to create/update tenant template in database",
properties={
"template_id": template.template_id,
"tenant_id": template.tenant_id,
"region": template.region
}
)
return template
except Exception as e:
await self.module_logger.log_error(
error="Failed to create template in database",
properties={
"template_id": template.template_id,
"error": str(e)
}
)

View File

@ -12,6 +12,7 @@ class MessageTemplateDoc(Document):
subject: str
body: str
is_active: bool = True
is_existing: bool = False
created_at: datetime = datetime.utcnow()
updated_at: Optional[datetime] = None
@ -21,7 +22,7 @@ class MessageTemplateDoc(Document):
"template_id",
"tenant_id",
"region"
]
]
class EmailSenderDoc(Document):
tenant_id: str

View File

@ -7,7 +7,7 @@ from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
from pydantic import BaseModel
from typing import Dict, List
import json
from datetime import datetime
from datetime import datetime, timedelta
router = APIRouter()
@ -127,7 +127,7 @@ def verify_tenant_access(template_id: str, user_tenant_id: str, template_message
dependencies=[Depends(admin_only)],
operation_id="create_global_template",
summary="Create a new global template (Admin Only)",
description="Create a new global template that all tenants can use",
description="Create a new global template that all tenants can use. If template already exists, it will be skipped.",
response_description="Success/failure response in creating the global template"
)
async def create_global_template(request: TemplateCreateRequest):
@ -139,10 +139,16 @@ async def create_global_template(request: TemplateCreateRequest):
body=request.body,
is_active=request.is_active
)
# check if the template is created or skipped (already exists)
is_skipped = hasattr(result, '_is_existing') and result._is_existing
return JSONResponse(
content={"message": "Global template created successfully", "template_id": request.template_id},
status_code=201
content={
"message": f"Global template {'skipped (already exists)' if is_skipped else 'created'} successfully",
"template_id": request.template_id,
"action": "skipped" if is_skipped else "created"
},
status_code=200 if is_skipped else 201
)
except ValueError as e:
raise HTTPException(status_code=400, detail=str(e))
@ -265,7 +271,7 @@ async def assign_templates_to_tenant(request: TemplateAssignRequest, payload: di
dependencies=[Depends(tenant_only)],
operation_id="create_tenant_template",
summary="Create a tenant template (Tenant Only)",
description="Create a new template for the current tenant",
description="Create a new template for the current tenant. If template already exists, it will be skipped.",
response_description="Success/failure response in creating the tenant template"
)
async def create_tenant_template(request: TemplateCreateRequest, payload: dict = Depends(tenant_only)):
@ -281,6 +287,9 @@ async def create_tenant_template(request: TemplateCreateRequest, payload: dict =
is_active=request.is_active
)
# check if the template is created or skipped (already exists)
is_skipped = hasattr(result, '_is_existing') and result._is_existing
if hasattr(result, 'dict'):
result_dict = result.dict()
for key, value in result_dict.items():
@ -299,8 +308,12 @@ async def create_tenant_template(request: TemplateCreateRequest, payload: dict =
}
return JSONResponse(
content={"message": "Tenant template created successfully", "result": result_dict},
status_code=201,
content={
"message": f"Tenant template {'skipped (already exists)' if is_skipped else 'created'} successfully",
"result": result_dict,
"action": "skipped" if is_skipped else "created"
},
status_code=200 if is_skipped else 201,
media_type="application/json"
)
except ValueError as e: