idconvert/backend/export/validator.py

78 lines
2.6 KiB
Python

from __future__ import annotations
import json
from core.exceptions import FileValidationError
MAX_FILE_SIZE_MB = 100
SUPPORTED_VERSIONS = {'1.0', '2.0'}
REQUIRED_FIELDS = {'document', 'styles', 'pages', 'fonts_used'}
class ExportValidator:
"""Validates idconvert_export.json before any parsing occurs."""
def validate(self, content: bytes) -> dict:
"""Run all validation checks. Returns parsed dict on success.
Raises FileValidationError on any failure.
"""
# 1. File size
size_mb = len(content) / (1024 * 1024)
if size_mb > MAX_FILE_SIZE_MB:
raise FileValidationError(
f'File exceeds {MAX_FILE_SIZE_MB}MB limit.',
'FILE_TOO_LARGE',
)
# 2. Valid JSON
try:
data = json.loads(content)
except (json.JSONDecodeError, UnicodeDecodeError):
raise FileValidationError(
'This does not appear to be a valid IDexport file. '
'Please run IDexport.jsx in InDesign and upload the '
'idconvert_export.json file it produces.',
'INVALID_JSON',
)
if not isinstance(data, dict):
raise FileValidationError(
'IDexport file has an unexpected format.',
'INVALID_FORMAT',
)
# 3. Generator signature
if data.get('generator') != 'IDexport':
raise FileValidationError(
'This JSON file was not produced by IDexport. '
'Please run IDexport.jsx in InDesign to generate '
'a compatible export file.',
'WRONG_GENERATOR',
)
# 4. Version check
if data.get('version') not in SUPPORTED_VERSIONS:
raise FileValidationError(
'This IDexport file was created with an incompatible version. '
'Please download the latest IDexport.jsx from IDconvert.',
'VERSION_MISMATCH',
)
# 5. Required top-level fields
for f in REQUIRED_FIELDS:
if f not in data:
raise FileValidationError(
f'IDexport file is missing required field: {f}. '
'Please re-run IDexport.jsx and try again.',
'MISSING_FIELD',
)
# 6. Pages must be a non-empty list
if not isinstance(data.get('pages'), list) or len(data['pages']) == 0:
raise FileValidationError(
'IDexport file contains no pages.',
'NO_PAGES',
)
return data