@@ -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
263254def create_error_response (error : DocumentProcessingError , status_code : int = 500 ) -> JSONResponse :
@@ -507,7 +498,7 @@ async def healthcheck():
507498async 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' )
820811async 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 (
0 commit comments