fix(export): /export/etherpad honors the :rev URL segment#7566
fix(export): /export/etherpad honors the :rev URL segment#7566JohnMcLear wants to merge 1 commit intoether:developfrom
Conversation
Fixes ether#5071. `/p/:pad/:rev/export/etherpad` has always ignored the rev parameter and returned the full pad history, unlike the txt/html export endpoints which use the same route but do respect rev. Users wanting to back up or inspect a snapshot of a pad at a specific rev got every later revision in the payload instead — both wasteful and a surprise when the downloaded .etherpad blob contained content that had supposedly been reverted. Change: - `exportEtherpad.getPadRaw(padId, readOnlyId, revNum?)` now takes an optional revNum. When supplied, it clamps to `min(revNum, pad.head)`, iterates only revs 0..effectiveHead, and ships a shallow-cloned pad object whose `head` and `atext` reflect the requested snapshot. The original live Pad is still passed to the `exportEtherpad` hook so plugin callbacks see the real document. - `ExportHandler` passes `req.params.rev` through on the `etherpad` type, matching the existing behavior of `txt` and `html`. - Chat history is intentionally left full (it is not rev-anchored). Adds three backend regression tests under `ExportEtherpad.ts`: - default (no revNum) still exports the full history - explicit revNum limits exported revs and rewrites the serialized head so re-import reconstructs the pad at that rev - revNum above head is treated as full history, preventing accidental truncation of short pads Out of scope: `getHTML(padID, rev)` on the API side is already honoring rev in current code (exportHtml.getPadHTML threads the parameter through), so the earlier report on that API call appears to be resolved. This PR does not touch it. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Review Summary by QodoFix /export/etherpad to honor :rev URL segment for revision-bounded exports
WalkthroughsDescription• /export/etherpad now respects the :rev URL segment to export bounded pad history • getPadRaw() accepts optional revNum parameter clamping exported revisions • Serialized pad object rewritten with bounded head and atext for correct re-import • Three regression tests added covering full history, bounded revisions, and edge cases Diagramflowchart LR
A["ExportHandler receives :rev param"] -->|passes revNum| B["getPadRaw with revNum"]
B -->|clamps to min| C["effectiveHead = min(revNum, pad.head)"]
C -->|iterates 0..effectiveHead| D["Filtered revisions exported"]
C -->|fetches bounded atext| E["Serialized pad with rewritten head/atext"]
E -->|preserves live pad| F["exportEtherpad hook sees full document"]
D --> G["Rev-bounded .etherpad file"]
F --> G
File Changes1. src/node/handler/ExportHandler.ts
|
Code Review by Qodo
1. Rev-bounded export undocumented
|
| // Honor the :rev URL segment on `.etherpad` exports the same way the | ||
| // other formats already do — revNum limits the serialized pad to revs | ||
| // 0..rev (issue #5071). | ||
| const pad = await exportEtherpad.getPadRaw(padId, readOnlyId, req.params.rev); | ||
| res.send(pad); |
There was a problem hiding this comment.
1. Rev-bounded export undocumented 📘 Rule violation ⚙ Maintainability
The PR changes public behavior by making /p/:pad/:rev/export/etherpad honor :rev and by sometimes serializing data["pad:<id>"] as a plain object rather than a Pad instance, but no documentation in doc/ is updated to reflect this. This can break or confuse API/plugin consumers who rely on the prior export semantics.
Agent Prompt
## Issue description
The PR changes `.etherpad` export semantics to honor `:rev` and changes the shape/semantics of the exported `data` payload (the exported pad record can be a plain JSON object when rev-bounded), but there is no corresponding documentation update under `doc/`.
## Issue Context
This behavior is externally observable via the `/p/:pad/:rev/export/etherpad` route and can also affect plugin authors via the `exportEtherpad` hook’s `data` object.
## Fix Focus Areas
- doc/api/hooks_server-side.md[970-991]
- src/node/handler/ExportHandler.ts[70-74]
- src/node/utils/ExportEtherpad.ts[24-73]
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
Summary
Fixes #5071. The
/p/:pad/:rev/export/etherpadroute has always ignored the:revURL segment and returned the full pad history, unlike thetxt/htmlexports on the same route which do respect rev. This broke timeslider's "Export current version (as Etherpad)" button and any integration trying to produce a rev-bounded.etherpadbackup or inspect a particular snapshot.The original maintainer reply on the issue (#5071 (comment)) concluded: "OK I think you have a point here. I'd consider it a feature request to make the etherpad export more useful to different use cases." — implementing that now.
Change
exportEtherpad.getPadRaw(padId, readOnlyId, revNum?)takes an optionalrevNum. When supplied it clamps tomin(revNum, pad.head), iterates only revs0..effectiveHead, and ships a shallow-cloned pad object whoseheadandatextreflect the requested snapshot. The original livePadis still passed to theexportEtherpadhook so plugin callbacks see the real document.ExportHandlerthreadsreq.params.revthrough on theetherpadtype, matching the existing behavior oftxt/html.Out of scope
The API-side
getHTML(padID, rev)call reported in the original issue is already honoring rev in current code (exportHtml.getPadHTMLthreads the parameter through viapad.getInternalRevisionAText). That concern appears to have been fixed between 1.8.13 (the version the reporter was on) and today; this PR does not touch it.Test plan
src/tests/backend/specs/ExportEtherpad.ts:pnpm run ts-checkclean locallyCloses #5071
🤖 Generated with Claude Code