Skip to content

feat: add create_domain(), and fix a slash-only fqdn - #7

Merged
sebartyr merged 2 commits into
mainfrom
feat/create-domain
Aug 24, 2026
Merged

feat: add create_domain(), and fix a slash-only fqdn#7
sebartyr merged 2 commits into
mainfrom
feat/create-domain

Conversation

@sebartyr

Copy link
Copy Markdown
Member

Two related changes on domains, from a user report.

fix: a slash-only fqdn produced an empty domain

Domain.from_api_response() validated the raw fqdn and stripped the trailing slashes afterwards, so {"fqdn": "/"} passed validation and produced Domain(domain=""). The stripped value is now what gets validated, and it raises the same InvalidResponseError as a missing field — one case for the caller to handle, not two.

The report also flagged _require_str(data, "fqdn", model="Domain") as reading the wrong key, on the reading that _mapping(data, model="Domain") maps the payload onto the dataclass. It does not: model= is only the label used to build the error message, _mapping() returns the payload unchanged, and "fqdn" is the key the API actually sends. No change needed there.

feat: create_domain()

PUT /v2/organisations/{ownerId}/applications/{appId}/vhosts/{domain}. The SDK could list domains and read the primary one, but not attach a new one.

domain = await client.create_domain("orga_xxx", "app_xxx", domain="app.example.com")
  • The name is stripped of its trailing slash before being percent-encoded, so it round-trips with Domain.domain and a path suffix such as example.com/api stays part of the vhost name instead of changing which endpoint the request reaches.
  • Some deployments answer the PUT with an empty body; the returned Domain then falls back to the requested name.

Named create_domain for consistency with create_application / create_tcp_redirection.

Tests

Four cases for create_domain (nominal, empty body, encoded path suffix, empty name rejected) and one parametrized case for the slash-only fqdn. Full suite, ruff check and mypy all pass.

CHANGELOG.md is untouched on purpose: per RELEASING.md, the section is written when the version is cut.

Domain.from_api_response() validated the raw fqdn and stripped the
trailing slashes afterwards, so a payload such as {"fqdn": "/"} passed
validation and produced Domain(domain=""). Validate the stripped value
instead, and raise the same InvalidResponseError as a missing field so
the caller has one case to handle.

Reported by a user who read model="Domain" as a schema mapping; it is
only the label used in error messages, and reading "fqdn" from the raw
payload is correct.
PUT /v2/organisations/{ownerId}/applications/{appId}/vhosts/{domain}.
The SDK could list domains and read the primary one, but not attach a
new one.

The name is stripped of its trailing slash before being percent-encoded,
so it round-trips with Domain.domain and a path suffix such as
"example.com/api" stays part of the vhost name instead of changing which
endpoint the request reaches. Some deployments answer with an empty
body, so the returned Domain falls back to the requested name.
@sebartyr
sebartyr merged commit a054813 into main Aug 24, 2026
4 checks passed
@sebartyr sebartyr mentioned this pull request Aug 24, 2026
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