Skip to content

Commit 69d375d

Browse files
committed
Implement clean API interface - remove legacy/fallback complexity
BREAKING CHANGE: Simplified API interface with direct parameters New clean interface: - file: Word template (.docx) - unchanged - data: JSON with template data - required - undefined_behavior: Optional string parameter - images: Optional JSON for images - linter_options: Optional JSON for linter settings Removed: - Complex TemplateRequest model detection - Legacy/enhanced mode distinction - Fallback and workaround code - request_data parameter (replaced by direct parameters) Benefits: - Much simpler and cleaner code - Easier to understand and use - Direct parameter mapping - No complex detection logic - Better API documentation
1 parent aefcd48 commit 69d375d

File tree

2 files changed

+46
-146
lines changed

2 files changed

+46
-146
lines changed

main.py

Lines changed: 45 additions & 145 deletions
Original file line numberDiff line numberDiff line change
@@ -249,15 +249,6 @@ class ImageData(BaseModel):
249249
height_px: Optional[int] = None # Height in pixels (alternative to mm)
250250

251251

252-
class TemplateRequest(BaseModel):
253-
"""Model for the complete template processing request"""
254-
template_data: Dict[str, Any] # The data to inject into the template
255-
# Optional images referenced in template
256-
images: Optional[Dict[str, ImageData]] = None
257-
# Override undefined variable behavior: "debug", "silent", "strict", "property_missing"
258-
undefined_behavior: Optional[str] = None
259-
# Optional linter configuration
260-
linter_options: Optional[LintOptions] = None
261252

262253

263254
def create_error_response(error: DocumentProcessingError, status_code: int = 500) -> JSONResponse:
@@ -507,7 +498,7 @@ async def healthcheck():
507498
async def get_version():
508499
"""Get the current version of the service"""
509500
return {
510-
"version": "1.5.0",
501+
"version": "1.5.1",
511502
"features": {
512503
"missing_fields_handling": True,
513504
"undefined_behavior_options": ["silent", "debug", "strict", "property_missing"],
@@ -818,33 +809,35 @@ async def _generate_lint_pdf_report(lint_result: LintResult, document_name: str,
818809

819810
@app.post('/api/v1/process-template-document')
820811
async def process_document_template(
821-
data: Json = Body(None),
822-
request_data: str = Body(None),
823-
file: UploadFile = File(...)
812+
file: UploadFile = File(...),
813+
data: Json = Body(...),
814+
undefined_behavior: Optional[str] = Body(None),
815+
images: Optional[Json] = Body(None),
816+
linter_options: Optional[Json] = Body(None)
824817
):
825818
"""
826819
Process a Word document template with data injection and convert to PDF.
827820
828-
Supports flexible parameter usage:
829-
- Use 'data' parameter with JSON object for simple requests
830-
- Use 'request_data' parameter with JSON string for complex requests
831-
- Both parameters support the same features: missing fields handling, images, and linter options
832-
833-
Both modes enable inline image support via base64 encoded PNGs and custom linter configuration.
834-
835-
**NEW: Integrated Template Linting**
836-
- Templates are automatically validated before processing (strict mode by default)
837-
- If validation fails, comprehensive error report is returned as PDF (or JSON if explicitly requested)
838-
- Linter options can be customized in enhanced mode via 'linter_options' field
839-
- Status code is always 200 for linting results (errors or success)
840-
- Processing only continues if template validation passes
841-
842-
Handles all stages with comprehensive error reporting:
843-
- File validation and upload
844-
- Template linting and syntax validation (NEW)
845-
- Data injection with variable checking
846-
- Image processing (when images provided)
821+
Parameters:
822+
- file: Word document template (.docx file)
823+
- data: JSON object with template data for variable substitution
824+
- undefined_behavior: Optional behavior for missing fields ("silent", "debug", "strict", "property_missing")
825+
- images: Optional JSON object with base64 encoded images
826+
- linter_options: Optional JSON object with template validation settings
827+
828+
Features:
829+
- Graceful missing fields handling (configurable behavior)
830+
- Inline image support via base64 encoded PNGs
831+
- Integrated template linting and validation
832+
- Comprehensive error reporting
847833
- PDF conversion with Gotenberg
834+
835+
Processing stages:
836+
1. File validation and upload
837+
2. Template linting and syntax validation
838+
3. Data injection with configurable undefined variable handling
839+
4. Image processing (when images provided)
840+
5. PDF conversion with Gotenberg
848841
"""
849842
file_path = None
850843
pdf_file_path = None
@@ -871,103 +864,13 @@ async def process_document_template(
871864
)
872865
return create_error_response(error, 400)
873866

874-
# Unified parameter processing - both 'data' and 'request_data' work identically
875-
template_data = None
876-
images_data = None
877-
api_undefined_behavior = None
878-
api_linter_options = None
867+
# Clean parameter processing
868+
template_data = data
869+
images_data = images
870+
api_undefined_behavior = undefined_behavior
871+
api_linter_options = linter_options
879872

880-
if request_data is not None:
881-
# Process request_data parameter (JSON string format)
882-
try:
883-
parsed_request = json.loads(request_data)
884-
if isinstance(parsed_request, dict):
885-
# Check if it's a TemplateRequest structure or simple data
886-
if "template_data" in parsed_request:
887-
# Full TemplateRequest structure
888-
request_obj = TemplateRequest(**parsed_request)
889-
template_data = request_obj.template_data
890-
images_data = request_obj.images
891-
api_undefined_behavior = request_obj.undefined_behavior
892-
api_linter_options = request_obj.linter_options
893-
else:
894-
# Simple data structure - treat as template_data
895-
template_data = parsed_request
896-
images_data = None
897-
api_undefined_behavior = None
898-
api_linter_options = None
899-
else:
900-
template_data = parsed_request
901-
images_data = None
902-
api_undefined_behavior = None
903-
api_linter_options = None
904-
905-
logger.info(f"Processing request_data with {len(template_data) if isinstance(template_data, dict) else 'non-dict'} data keys and {len(images_data or {})} images")
906-
except json.JSONDecodeError as e:
907-
error = TemplateProcessingError(
908-
message=f"Invalid JSON in request_data: {str(e)}",
909-
error_type="invalid_json",
910-
details={
911-
"json_error": str(e),
912-
"suggestion": "Ensure request_data is valid JSON"
913-
}
914-
)
915-
return create_error_response(error, 400)
916-
except Exception as e:
917-
error = TemplateProcessingError(
918-
message=f"Invalid request structure: {str(e)}",
919-
error_type="invalid_request_structure",
920-
details={
921-
"error": str(e),
922-
"suggestion": "Ensure request follows valid JSON structure"
923-
}
924-
)
925-
return create_error_response(error, 400)
926-
elif data is not None:
927-
# Process data parameter (can be JSON object or TemplateRequest-like structure)
928-
if isinstance(data, dict):
929-
# Check if it's a TemplateRequest structure or simple data
930-
if "template_data" in data:
931-
# TemplateRequest-like structure in data parameter
932-
try:
933-
request_obj = TemplateRequest(**data)
934-
template_data = request_obj.template_data
935-
images_data = request_obj.images
936-
api_undefined_behavior = request_obj.undefined_behavior
937-
api_linter_options = request_obj.linter_options
938-
except Exception as e:
939-
# Fallback to treating as simple template data
940-
template_data = data
941-
images_data = None
942-
api_undefined_behavior = None
943-
api_linter_options = None
944-
else:
945-
# Simple data structure - treat as template_data
946-
template_data = data
947-
images_data = None
948-
api_undefined_behavior = None
949-
api_linter_options = None
950-
else:
951-
template_data = data
952-
images_data = None
953-
api_undefined_behavior = None
954-
api_linter_options = None
955-
956-
logger.info(f"Processing data parameter with {len(template_data) if isinstance(template_data, dict) else 'non-dict'} data keys and {len(images_data or {})} images")
957-
else:
958-
# No data provided at all
959-
error = TemplateProcessingError(
960-
message="No template data provided. Use either 'data' parameter or 'request_data' parameter",
961-
error_type="missing_template_data",
962-
details={
963-
"requirement": "Provide either 'data' (JSON object) or 'request_data' (JSON string)",
964-
"examples": {
965-
"legacy": '{"name": "John Doe"}',
966-
"enhanced": '{"template_data": {"name": "John Doe"}, "images": {}}'
967-
}
968-
}
969-
)
970-
return create_error_response(error, 400)
873+
logger.info(f"Processing template with {len(template_data) if isinstance(template_data, dict) else 'non-dict'} data keys and {len(images_data or {})} images")
971874

972875
if template_data is None or (isinstance(template_data, (list, dict)) and len(template_data) == 0):
973876
error = TemplateProcessingError(
@@ -980,23 +883,20 @@ async def process_document_template(
980883
)
981884
return create_error_response(error, 400)
982885

983-
# Additional validation for legacy mode only (enhanced mode already validated)
984-
if request_data is None:
985-
try:
986-
if isinstance(template_data, str):
987-
template_data = json.loads(template_data)
988-
except json.JSONDecodeError as e:
989-
error = TemplateProcessingError(
990-
message=f"Invalid JSON data: {str(e)}",
991-
error_type="invalid_json",
992-
details={
993-
"json_error": str(e),
994-
"line": getattr(e, 'lineno', None),
995-
"column": getattr(e, 'colno', None),
996-
"suggestion": "Ensure the data parameter contains valid JSON"
997-
}
998-
)
999-
return create_error_response(error, 400)
886+
# Validate that template_data is JSON serializable
887+
try:
888+
json.dumps(template_data)
889+
except (TypeError, ValueError) as e:
890+
error = TemplateProcessingError(
891+
message=f"Invalid template data: {str(e)}",
892+
error_type="invalid_json_data",
893+
details={
894+
"json_error": str(e),
895+
"data_type": type(template_data).__name__,
896+
"suggestion": "Ensure all data values are JSON serializable (strings, numbers, booleans, lists, dicts)"
897+
}
898+
)
899+
return create_error_response(error, 400)
1000900

1001901
# Setup file paths
1002902
sanitized_filename = "".join(

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "document-templating-service"
7-
version = "1.5.0"
7+
version = "1.5.1"
88
description = "A FastAPI service for processing Word document templates with Jinja2 and converting to PDF"
99
authors = [{name = "Document Templating Service Team"}]
1010
license = {text = "MIT"}

0 commit comments

Comments
 (0)