Skip to content

feat(db): add GET /api/db/collection/export (zip / installable .xar)#68

Draft
joewiz wants to merge 1 commit into
eXist-db:developfrom
joewiz:feat/collection-export
Draft

feat(db): add GET /api/db/collection/export (zip / installable .xar)#68
joewiz wants to merge 1 commit into
eXist-db:developfrom
joewiz:feat/collection-export

Conversation

@joewiz

@joewiz joewiz commented Jun 20, 2026

Copy link
Copy Markdown
Member

[This PR was co-authored with Claude Code. -Joe]

⛔ Draft — blocked on eXist-db/exist#6493. This endpoint calls compression:zip with a serialization-options argument that does not exist in any released eXist. Until eXist-db/exist#6493 is reviewed, merged, and present in the CI image, db-core.xqm cannot compile (XPST0017) and CI on existdb/existdb:latest stays red. This PR is intentionally parked pending that review.

Summary

Adds GET /api/db/collection/export — download a whole collection subtree as a ZIP (default) or an installable EXPath .xar, with caller-chosen serialization parameters applied to the XML resources. This is the server side of the existdb-oxygen-plugin / eXide "download collection / download package" features (both need per-call serialization control over the archived XML).

Endpoint

GET /api/db/collection/export
    ?path=/db/apps/myapp           (required) collection to export, recursively
    &format=zip|xar                (default zip)
    &indent=…&omit-xml-declaration=…              (W3C params: plain names)
    &exist:expand-xincludes=…                     (eXist extensions: "exist:" prefix)
    …                                             (+ the full serialization vocabulary)
  • Response body is the archive; Content-Type: application/zip, Content-Disposition: attachment.
  • zip<collectionName>.zip. xar<abbrev>-<version>.xar read from the collection's expath-pkg.xml, with expath-pkg.xml/repo.xml at the archive root so it installs via repo:install-and-deploy. A xar request for a collection without expath-pkg.xml is a clean 400.
  • Binary-safe: binary resources are stored byte-for-byte; only XML resources go through the serializer. Omitted serialization params defer to the conf.xml defaults.
  • Read permissions are honored (it runs as the authenticated subject). 404 for a missing collection, 400 for a missing path.

How it works

The handler delegates to eXist's compression:zip, passing the caller's serialization parameters as its 5th argument — the <output:serialization-parameters> element built by the existing dbc:serialization-params helper (the exist:-namespace expand-xincludes form from the resource-consolidation work). Core does the recursion, prefix-stripping, binary-safety, and per-resource serialization; the handler is a thin wrapper. db-core.xqm gains dbc:export-collection (roaster-independent); db.xqm gains the db:export-collection route handler.

Serialization parameters follow the same convention as /api/db/resource: the W3C parameters keep their plain names (indent, omit-xml-declaration, …) and the implementation-defined eXist extensions carry an exist: prefix on the wire (exist:expand-xincludes, exist:highlight-matches, …), mirroring the exist: serialization namespace. That convention is shared with /api/db/resource (merged in #59).

Dependency

  • Hard blocker — eXist-db/exist#6493 (compression: add serialization-options argument to compression:zip / compression:tar). The endpoint's 5-argument compression:zip call has no fallback: the signature simply does not exist without this core change, so the module fails to compile and the whole /api/db surface 500s. This PR waits on #6493 being reviewed and merged, then reaching the existdb/existdb:latest CI image.
  • feat(db): consolidate resource content onto a binary-safe /api/db/resource #59 (the /api/db/resource consolidation this built on) has merged, so this branch is now rebased onto develop as a single export commit — it inherits the merged exist: serializer convention and the dbc:store binary-under-XML-mime workaround (the eXist-db/exist#6497 fix), which is why #6497 does not separately gate this PR. #6493 is the only remaining blocker.

Testing

src/test/cypress/e2e/db_export.cy.js — 13 cases, all green against a build carrying eXist-db/exist#6493:

  • zip headers + Content-Disposition filename; archive-root-relative entry names across a subtree
  • binary stored byte-for-byte (proven with content the serializer would otherwise escape/reflow)
  • indent (compact vs pretty), omit-xml-declaration, exist:expand-xincludes (preserve vs expand) applied to the archived XML; no-params = conf.xml default
  • xar filename + descriptors at the archive root; xar-without-expath-pkg.xml400; missing path400; missing collection → 404

Fixtures are seeded via native eXist REST so the export tests don't depend on the resource-store endpoint.

Notes

  • Bumps the OpenAPI contract info.version to 0.15.0 (additive endpoint, above develop's 0.14.0).
  • Adds jszip as a dev-dependency, used by the spec to assert archive contents.

@joewiz joewiz force-pushed the feat/collection-export branch from 7512021 to 9ef350a Compare July 8, 2026 03:48
@codacy-production

codacy-production Bot commented Jul 8, 2026

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 58 complexity · 3 duplication

Metric Results
Complexity 58
Duplication 3

View in Codacy

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

@joewiz joewiz force-pushed the feat/collection-export branch from 9ef350a to 74da078 Compare July 8, 2026 20:17
Export a collection subtree as a ZIP (default) or an installable EXPath
.xar, with caller-chosen serialization params (indent, omit-xml-declaration,
expand-xincludes, and the full vocabulary) applied to XML resources only;
binary resources are stored byte-for-byte. format=xar names the archive
<abbrev>-<version>.xar from expath-pkg.xml and places expath-pkg.xml/repo.xml
at the archive root so it installs via repo:install-and-deploy (400 if the
collection has no descriptor).

Delegates to eXist's compression:zip, passing the serialization params as
its 5th argument; reuses the dbc:serialization-params helper from the
resource-consolidation work (exist:-namespace expand-xincludes). Requires an
eXist base with the compression serialization-options argument
(eXist-db/exist#6493).

Bumps the OpenAPI contract version (info.version) to 0.10.0 for the additive
endpoint.

Tests: cypress db_export.cy.js (13 cases), green against the #6493 build;
fixtures seed via native eXist REST to stay independent of the resource-store
path.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@joewiz joewiz force-pushed the feat/collection-export branch from 74da078 to 72cd777 Compare July 12, 2026 01:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant