feat(packages): implement multipart .xar upload on POST /api/packages/install#49
Conversation
duncdrum
left a comment
There was a problem hiding this comment.
Just a note we have encoding: binary and the basic form data append to model the upload via cy.request what exactly isn't working?
https://filiphric.com/cypress-basics-uploading-file#upload-via-api
|
[This response was co-authored with Claude Code. -Joe] You're right — the Worth noting it's not throwaway even though package-install is converging onto the core servlet (eXist-db/exist#6247, per the api-/exist-strategy plan): #49 stays the working surface until |
|
|
||
| // cy.request returns the response body as a raw string under | ||
| // encoding: 'binary', so parse JSON defensively (handles either form). | ||
| const asJson = (response) => |
There was a problem hiding this comment.
I think this is still missing the iniitial formdata blog construction to append to in the request.
duncdrum
left a comment
There was a problem hiding this comment.
test are failing, we seem to be missing construction of the initial formdata blob to append to
0b18363 to
099e162
Compare
…/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>
…url/cy.exec Per review (duncdrum): cy.request can do the binary multipart upload natively with encoding: 'binary' + a hand-built form-data envelope, so the cy.exec+curl workaround (and its curl-on-runner / hardcoded-creds dependency) is unnecessary. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Model the multipart upload through cy.request (auto multipart/form-data
boundary from a FormData body) rather than a cross-origin native fetch,
per Duncan's review. cy.request returns an empty body on the upload
response, so assert only its HTTP status and read the deployed package's
identity (name, version, abbrev, target) from a follow-up
GET /api/packages/{name}. Keeps the whole flow same-origin through the
Cypress proxy and out of the browser's CORS path.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017C5N8P9iJKm8MYA9frPkAf
099e162 to
faa4a5c
Compare
Up to standards ✅🟢 Issues
|
| Metric | Results |
|---|---|
| Complexity | 14 |
| Duplication | 2 |
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.
|
[This response was prompted by Joe, drafted by Claude Code, and reviewed by Joe.] Thanks @duncdrum — you were right that this belonged on 1. The upload now goes through 2. The branch was badly stale. It was cut at Verified against a clean |
[This PR was co-authored with Claude Code. -Joe]
Implement multipart
.xarupload onPOST /api/packages/installFrom the existdb-oxygen-plugin (oxex) Package Manager tasking. The endpoint already declared a
multipart/form-databody with a binaryfilefield, butpackages:installonly 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
filepart:packages:install-from-upload: store the bytes to/db/system/repo,repo:install-and-deploy-from-db, clean up the temp.xar, and returnname/version/targetfrom the deployed descriptor.repo:install-and-deploy-from-dbis 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.packages:install-from-registry, behavior unchanged).Response shape matches across both paths:
{ success, result: { name, version, target } }.Verification
.xarinstalls + deploys (appears inGET /api/packages); re-upload replaces idempotently; temp.xaris cleaned up; JSON path unchanged; malformed JSON still errors as before. Verified on the integration instance and deployed to the oxex test instance.packages.cy.js. The happy-path upload is driven viacy.exec+ curl rather thancy.request: Cypress mangles raw binary request bodies (Buffer↔JSON serialization), so a real multipart file upload can't go throughcy.requestreliably — curl sends correct bytes. Fixture:src/test/cypress/fixtures/test-multipart.xar(566-byte minimal EXPath package, force-added past the*.xargitignore).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.