refactor: remove XML export, JSON-only for .rfflow files

- Remove XML builder, format query param, and XML tests
- Simplify ExportFlowModal (no format picker)
- Simplify rfflowParser (JSON-only)
- Remove format field from schemas and types

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
chihlasm
2026-03-06 00:56:07 -05:00
parent 39677a3841
commit 88c1553c5d
8 changed files with 22 additions and 293 deletions

View File

@@ -1,8 +1,6 @@
"""Flow export/import endpoints (.rfflow files)."""
import json
import logging
import re
import xml.etree.ElementTree as ET
from datetime import datetime, timezone
from typing import Annotated, Optional
from uuid import UUID
@@ -43,45 +41,6 @@ def _slugify(name: str) -> str:
return re.sub(r'[-\s]+', '-', slug)
def _build_xml(envelope: FlowExportEnvelope) -> str:
"""Build XML representation of an .rfflow export."""
root = ET.Element("rfflow")
root.set("version", envelope.rfflow_version)
ET.SubElement(root, "exported_at").text = envelope.exported_at.isoformat()
ET.SubElement(root, "source_app").text = envelope.source_app
ET.SubElement(root, "format").text = "xml"
flow_el = ET.SubElement(root, "flow")
flow = envelope.flow
ET.SubElement(flow_el, "name").text = flow.name
ET.SubElement(flow_el, "description").text = flow.description or ""
ET.SubElement(flow_el, "tree_type").text = flow.tree_type
ET.SubElement(flow_el, "version").text = str(flow.version)
ET.SubElement(flow_el, "author_name").text = flow.author_name or ""
if flow.category:
cat_el = ET.SubElement(flow_el, "category")
ET.SubElement(cat_el, "name").text = flow.category.name
ET.SubElement(cat_el, "slug").text = flow.category.slug
tags_el = ET.SubElement(flow_el, "tags")
for tag in flow.tags:
ET.SubElement(tags_el, "tag").text = tag
# Store tree_structure as JSON string in CDATA-safe text
ts_el = ET.SubElement(flow_el, "tree_structure")
ts_el.text = json.dumps(flow.tree_structure)
if flow.intake_form:
if_el = ET.SubElement(flow_el, "intake_form")
if_el.text = json.dumps(flow.intake_form)
ET.indent(root)
return ET.tostring(root, encoding="unicode", xml_declaration=True)
# --- Export ---
@router.get("/{tree_id}/export")
@@ -89,9 +48,8 @@ async def export_tree(
tree_id: UUID,
db: Annotated[AsyncSession, Depends(get_db)],
current_user: Annotated[User, Depends(get_current_active_user)],
format: str = Query("json", pattern="^(json|xml)$"),
):
"""Export a tree as a downloadable .rfflow file (JSON or XML)."""
"""Export a tree as a downloadable .rfflow JSON file."""
# Load tree with relationships + author name
result = await db.execute(
select(Tree)
@@ -139,25 +97,15 @@ async def export_tree(
rfflow_version="1.0",
exported_at=datetime.now(timezone.utc),
source_app="ResolutionFlow",
format=format,
flow=flow_data,
)
slug = _slugify(tree.name)
# Audit log
await log_audit(db, current_user.id, "tree.export", "tree", tree.id, {"format": format})
await log_audit(db, current_user.id, "tree.export", "tree", tree.id)
await db.commit()
if format == "xml":
content = _build_xml(envelope)
return Response(
content=content,
media_type="application/xml",
headers={"Content-Disposition": f'attachment; filename="{slug}.rfflow"'},
)
# JSON
content = envelope.model_dump_json(indent=2)
return Response(
content=content,

View File

@@ -1,6 +1,6 @@
"""Schemas for .rfflow file export and import."""
from datetime import datetime
from typing import Optional, Any, Literal
from typing import Optional, Any
from pydantic import BaseModel, Field
from app.schemas.tree import TreeType
@@ -30,7 +30,6 @@ class FlowExportEnvelope(BaseModel):
rfflow_version: str = "1.0"
exported_at: datetime
source_app: str = "ResolutionFlow"
format: Literal["json", "xml"] = "json"
flow: FlowExportData
@@ -39,7 +38,6 @@ class FlowImportRequest(BaseModel):
rfflow_version: str = Field(..., description="Must be '1.0'")
exported_at: datetime
source_app: str = "ResolutionFlow"
format: Literal["json", "xml"] = "json"
flow: FlowExportData