Skip to content

feat(packages): implement multipart .xar upload on POST /api/packages/install#49

Open
joewiz wants to merge 2 commits into
eXist-db:developfrom
joewiz:feat/packages-multipart-upload
Open

feat(packages): implement multipart .xar upload on POST /api/packages/install#49
joewiz wants to merge 2 commits into
eXist-db:developfrom
joewiz:feat/packages-multipart-upload

Conversation

@joewiz

@joewiz joewiz commented Jun 6, 2026

Copy link
Copy Markdown
Member

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

Implement multipart .xar upload on POST /api/packages/install

From the existdb-oxygen-plugin (oxex) Package Manager tasking. The endpoint already declared a multipart/form-data body with a binary file field, but packages:install only implemented the JSON registry path ({name, url, version}repo:install-and-deploy). A file upload fell straight through to {"error": "Missing required fields: name, url"} — so clients could install from a registry by name but could not upload and install a locally built .xar, the headline deploy-a-built-app workflow for editor clients.

Change

Dispatch on the presence of a file part:

  • multipart uploadpackages:install-from-upload: store the bytes to /db/system/repo, repo:install-and-deploy-from-db, clean up the temp .xar, and return name/version/target from the deployed descriptor. repo:install-and-deploy-from-db is idempotent, so re-uploading a new build of an already-installed package replaces it — no explicit pre-removal needed. Malformed uploads return a caught {success: false, error: {...}} with HTTP 400, not a 500.
  • JSON body → the existing registry path (refactored into packages:install-from-registry, behavior unchanged).

Response shape matches across both paths: { success, result: { name, version, target } }.

Verification

  • Multipart upload of a fixture .xar installs + deploys (appears in GET /api/packages); re-upload replaces idempotently; temp .xar is cleaned up; JSON path unchanged; malformed JSON still errors as before. Verified on the integration instance and deployed to the oxex test instance.
  • Cypress: 5 new tests in packages.cy.js. The happy-path upload is driven via cy.exec + curl rather than cy.request: Cypress mangles raw binary request bodies (Buffer↔JSON serialization), so a real multipart file upload can't go through cy.request reliably — curl sends correct bytes. Fixture: src/test/cypress/fixtures/test-multipart.xar (566-byte minimal EXPath package, force-added past the *.xar gitignore).

api.json already declared the multipart requestBody and the 200/400 responses, so no spec change was needed.

Downstream

Unblocks the oxygen plugin's "Install .xar…" file-chooser; same local-deploy capability for eXide, vscode-existdb, and the notebook kernel over the shared HTTP contract.

joewiz and others added 2 commits June 6, 2026 12:37
…/install

The endpoint declared a multipart/form-data body with a binary `file`
field, but packages:install only implemented the JSON registry path
({name,url,version} -> repo:install-and-deploy); a file upload fell
through to {"error":"Missing required fields: name, url"}. So clients
could install from a registry by name but could not upload and install a
locally built .xar -- the headline deploy-a-built-app workflow for
editor clients (Oxygen plugin, eXide, vscode, notebook).

Dispatch on the presence of a `file` part: multipart upload ->
packages:install-from-upload (store bytes to /db/system/repo,
repo:install-and-deploy-from-db, clean up the temp .xar, return
name/version/target from the deployed descriptor); else the existing
JSON registry path (refactored into packages:install-from-registry,
unchanged). repo:install-and-deploy-from-db is idempotent, so
re-uploading a new build of an installed package replaces it without
explicit pre-removal. Malformed uploads return a caught
{success:false, error:{...}} with HTTP 400, not a 500.

Verified: multipart upload of a fixture .xar installs + deploys (appears
in GET /api/packages), re-upload replaces idempotently, temp .xar is
cleaned up, the JSON path is unchanged, and a malformed JSON body still
errors as before.

Cypress: 5 new tests in packages.cy.js. The happy-path upload is driven
via cy.exec + curl rather than cy.request -- Cypress mangles raw binary
request bodies (Buffer<->JSON serialization), so a real multipart file
upload can't go through cy.request reliably; curl sends correct bytes.
Fixture: src/test/cypress/fixtures/test-multipart.xar (566-byte minimal
EXPath package).

Refs oxex multipart-install tasking (2026-06-06).
…pload

Align the multipart upload path with how xst, the dashboard, and
atom-editor-support install a .xar:

- Pass the public registry's /find endpoint to repo:install-and-deploy-from-db
  so transitive dependencies are resolved from the registry during install
  (previously the 1-arg form was used, so an upload with unmet dependencies
  failed instead of resolving them).
- Undeploy + remove any previously installed copy of the same package before
  reinstalling, rather than relying solely on install-and-deploy-from-db
  idempotency. The package name is read from the upload's expath-pkg.xml via a
  new packages:xar-package-name helper.

Verified on a clean eXist 7.0.0-beta3: fresh install, re-install (pre-removal
path), and temp-.xar cleanup all succeed; xar-package-name extracts the
package name from the uploaded descriptor.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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