Skip to content

fix: align CLI requests with the v3 OpenAPI spec - #9

Merged
kotapeter merged 2 commits into
mainfrom
fix/v3-openapi-compat
Oct 18, 2026
Merged

kotapeter merged 2 commits into
mainfrom
fix/v3-openapi-compat

Conversation

@kotapeter

Copy link
Copy Markdown
Member

Fixes #8. Thanks @netlemur for the detailed report.

What changed

I checked every command against the v3 OpenAPI spec: each command ran against a local server that captured its request, and the method, path, query and body were validated against the spec's schemas. Besides the seven commands in the issue, this found commands with wrong paths, missing required bodies, and fields the API ignores.

Requests the API rejected or misrouted

Command Problem Fix
apps processes metrics cpu-usage|cpu-limit|memory-usage|memory-limit|instance-count Path was missing /processes/ (404) Correct path
apps processes create Wrong field names, required fields missing Sends display_name, entrypoint, resource_type_id, scaling_strategy. --resource-type is now required and --instances defaults to 1. New --schedule (cron) and --job-start-policy (job) flags
apps processes update name, command, resource_type, instances were ignored Same mapping as create
apps|static-sites|load-balancers domains add Sent name instead of domain_name Sends domain_name
apps|static-sites|load-balancers domains update Sent no body New required --name
apps tcp-proxies create, apps private-ports create process_id missing, protocol not in spec New required --process-id, removed --protocol
apps deployments rollback The endpoint takes no body and always rolls back to the most recent successful deployment Removed --deployment-id
apps|static-sites deployments trigger tag is not in the spec Removed --tag
apps clone display_name is required --display-name is required
apps ip-restriction update The PUT needs all three fields --type, --ip-list, --enabled are required; --enabled accepts only true or false
databases ip-restriction update Sent allow, deny, enabled Same flags as the apps command: --type, --ip-list, --enabled
databases connections internal-create Sent application_id --target-id (required) and --target-type (default app)
databases reset-password Required body missing New required --new-password
databases backups create display_name missing New required --name
databases backups restore Required body missing Sends a JSON body
databases metrics all-storage|used-storage The endpoint takes no query and returns { value }, but the CLI sent from/to/interval and printed a time series Plain get command
load-balancers destinations create Sent application_id and port, service_type missing --service-type (required), --service-id, --url, --weight, --latitude, --longitude
object-storage cors-policies create|update Update sent strings instead of arrays; origins and methods are required Comma-separated values sent as arrays, both flags required
object-storage objects delete keys sent as a string Required --keys, sent as an array. Now that it really deletes, it also requires --confirm, like every other delete
pipelines promote Required body missing New required --source-app-id and --target-app-ids
pipelines preview enable|update enable sent no body; update sent branch and auto_deploy Both take --repo-url, --auto-create-app, --delete-stale-apps, --stale-app-days; the boolean flags accept only true or false
pipelines stages create, projects update, projects services add A field the spec requires was optional Flag is required

Pagination

List commands sent limit and offset to every endpoint. These endpoints don't define them: resources clusters|db-types|process-types|rbac-permissions|rbac-roles, users list, git providers, databases backups list, databases connections internal-list, object-storage cors-policies list and object-storage objects list. The list factory now takes paginated: false, and those commands no longer send the parameters or show --page/--per-page.

Other fixes

  • object-storage delete crashed with Unexpected end of JSON input: the endpoint returns 202 with an empty body, and the client parsed every non-204 response as JSON. The client now returns undefined for an empty body.
  • src/client/endpoints/applications.ts built /v3/v3/... paths, as noted in the issue. BASE is now /applications. Nothing imports src/client/endpoints, so a follow-up could remove it.
  • The e2e suite couldn't run: tests/e2e/helpers/cli.ts pointed at tests/dist/index.js, and the workflow matrix used e2e/tests/... paths. Fixed both, and added the four application-* suites that were missing from the matrix. The e2e cleanup helpers also looked for deployment statuses the API doesn't use (in_progress and others instead of waiting/inProgress), so they never cancelled running deployments before deleting.

Breaking changes

None of these flags ever produced a valid request, but scripts that pass them will now get an "unknown option" error:

  • apps deployments rollback --deployment-id
  • apps|static-sites deployments trigger --tag
  • apps private-ports create --protocol
  • databases ip-restriction update --allow/--deny (use --type and --ip-list)
  • databases connections internal-create --application-id (use --target-id)
  • load-balancers destinations create --application-id/--port (use --service-type and --service-id)
  • pipelines preview update --branch/--auto-deploy

Some commands also have new required flags; see the table above.

Tests

  • E2E coverage for every changed command: process CRUD and metrics, TCP proxies, private ports, rollback, domain add/update for apps, static sites and load balancers, database IP restriction, internal connections, storage metrics, backups, password reset, load balancer destinations, CORS update, object delete, pipeline promote and preview, and the non-paginated lists.
  • Where success depends on account state (rollback or promote without a successful deployment, backup create on clusters without snapshot support, adding a custom domain to a load balancer, renaming a static site system domain), the test asserts only that the API accepts the request shape. databases backups restore is skipped when the database has no backup to restore.
  • Unit tests for paginated: false, the empty 202 response, and the preview body.
  • npm run typecheck, npm run lint, npm run format:check and the unit tests pass. The full e2e suite was run against a Sevalla test account: 165 passed, 1 skipped (backup restore, as the test cluster has no backups), 0 failed.

Follow-ups

  • object-storage objects list lists only the first page: the endpoint uses cursor pagination (cursor, prefix, limit), which the CLI doesn't support yet.
  • databases reset-password --new-password puts the password in shell history; reading it from stdin or an env var would avoid that.
  • Remove the unused src/client/endpoints helpers.

Validate every command's request against https://api.sevalla.com/v3/openapi.json
and fix the mismatches: wrong paths (process metrics), wrong or missing body
fields (processes, domains, TCP proxies, private ports, IP restrictions,
internal connections, load balancer destinations, CORS policies, object
delete, backups, password reset, pipeline promote and preview), required
fields that were optional flags, and pagination params sent to endpoints
that don't accept them.

Also handle empty 202 responses in the API client, fix the duplicated /v3
prefix in the unused endpoint helpers, and make the e2e suite runnable
(CLI path, workflow paths, deployment statuses) with coverage for every
changed command.

Fixes #8
- object-storage objects delete now needs --confirm like every other delete
- --enabled on ip-restriction updates accepts only true/false
- pipeline preview boolean flags reject values other than true/false
- e2e: state-dependent asserts now require a real API refusal (not a
  crash, timeout or auth error), and custom-domain tests use a domain
  the team owns
@kotapeter
kotapeter merged commit 32b8290 into main Oct 18, 2026
3 checks passed
@kotapeter
kotapeter deleted the fix/v3-openapi-compat branch October 18, 2026 08:45
@github-actions

Copy link
Copy Markdown

🎉 This PR is included in version 1.10.1 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Seven commands send request bodies the v3 API rejects with "Request doesn't match the schema"

2 participants