Skip to content

v2.0.0: resources realigned with the real API

Latest

Choose a tag to compare

@welcoMattic welcoMattic released this 26 Aug 10:00
· 1 commit to main since this release
edd40b4

This release corrects resources that never worked against the real Clever Cloud
API. Every path, payload shape and status code below was verified by calling the
live API, not inferred from documentation.

The major bump is deliberate. 1.0.0 promised that breaking source compatibility
would trigger one, and this release removes public symbols. Several of them
described endpoints or fields that do not exist, so code using them was already
failing at runtime; but Deployment::$author and ProductsResource::countries()
compile today and will not after upgrading. Read the breaking changes before
bumping.

Breaking changes

  • Resource\V2\LogsResource is now Resource\V4\LogsResource. No
    compatibility alias is provided. The class targeted a V2 path the API does not
    serve, so every call returned 404 and no working code can be relying on it.
    $client->logs is unchanged.
  • Model\Country is removed. It described a shape the API never sends. See
    countries() below.
  • ProductsResource::countries() returns array<string, string> instead of
    list<Country>: a map of upper-case English country name to ISO 3166-1
    alpha-2 code, which is what the endpoint actually answers.
  • LogEntry::$stream is removed, replaced by $service. Constructor
    parameter order changed, so positional construction of LogEntry breaks.
  • User::$firstname and User::$lastname are removed. /v2/self returns a
    single name field; those two were always null.
  • Deployment::$author is now ?DeploymentAuthor instead of ?string.
  • InstancesResource is now V2-based and only exposes list(). The 1.0.0
    version targeted V4 routes (/v4/products/instances*) that Clever Cloud does
    not expose, so every method returned 404. The redundant
    get(type[, version]), flavors(type), and types() methods are removed;
    call list() and filter client-side on $instanceType->type for per-type
    data. (The catalogue payload is the same as $client->products->instances().)

Fixed

  • Log streaming worked for nobody. LogsResource built
    /v2/organisations/{org}/applications/{app}/logs, which answers 404. The real
    endpoint is /v4/logs/organisations/{ownerId}/applications/{appId}/logs and
    speaks text/event-stream. There is no /self form, so the personal
    organisation is addressed by its own user_<uuid> id; passing a null owner
    now resolves it through GET /v2/self, costing one extra request.
  • LogEntry mapped almost nothing. Its #[MapFrom] attributes asked for
    instance_id, application_id and deployment_id while the API sends
    camelCase, so those three were always null. Properties now match the wire
    format, and the previously dropped id, priority, commitId, region and
    version are exposed.
  • LogsResource::query() could not work as a plain GET, since the endpoint
    only speaks SSE. It now consumes the stream and stops early. It also takes a
    $maxDurationSeconds budget (default 10), which is what actually guarantees
    the method returns: the endpoint never closes an idle stream, it emits
    HEARTBEAT forever, and no combination of since, until and limit makes
    it hang up. Pass a since filter, or the call degenerates into a live tail.
  • AddonsResource::plans() requested /v2/products/addonproviders/{id}/plans
    (404). Plans are nested in the provider payload; it now reads them from there.
  • ProductsResource::countries() threw a TypeError because it hydrated a
    JSON object as a list of models.
  • DeploymentsResource listings always threw a TypeError: the API sends
    author as an object, not a string.
  • ApiTokensResource paths and documented auth mode were both wrong. The
    routes are /api-tokens, not /v2/api-tokens (404). And the gateway requires
    OAuth 1.0a, answering 400 must start with "OAuth " for a Bearer header, the
    opposite of what the docs claimed. Minting a Bearer token is what these
    endpoints are for, so a token-authenticated client cannot manage tokens.
  • User mapping had the same snake_case defect as LogEntry, leaving
    preferredMfa, hasPassword, canPay, emailValidated and creationDate
    null on every response.
  • Documentation claimed logs required OAuth 1.0a and that API tokens always
    got a 404. That was a misdiagnosis of the broken path: Bearer tokens stream
    and query logs correctly through api-bridge.clever-cloud.com.
  • The OAuth 1.0a CLI example sent oob as its callback, which Clever Cloud
    rejects with HTTP 500. It now sends a real URL and reads oauth_verifier back
    from the browser's address bar, with CC_OAUTH_CALLBACK to override the host.
  • api-bridge demo and docs now explain the 13502 callback rejection. The
    callback is validated against the scheme and host of the consumer's Base URL
    (port and path are not checked), so a consumer registered on http:// refuses
    an https:// callback. The demo surfaces the mismatch and the clever oauth-consumers update command to fix it instead of only echoing the error.
  • README no longer credits PHP 8.5 with features that landed earlier: enums
    (8.1), readonly classes (8.2), property hooks and asymmetric visibility (8.4).
    The 8.5 floor is a support choice, not a technical requirement.
  • README no longer claims Clever Cloud's full v2 + v4 surface, which its own
    Roadmap section contradicts, and its status block now reflects 2.0.

Added

  • docs/ - full reference documentation. Resource pages with verified
    signatures + HTTP paths, guides for getting started, authentication,
    configuration, error handling, live log streaming, and testing patterns.
  • Model\DeploymentAuthor DTO (id, name).
  • Deployment::$instances, which the payload carries and the model dropped.
  • LogStream accepts an optional $maxDurationSeconds to bound iteration,
    checked on every chunk so heartbeat-only traffic still honours the budget.
  • Regression tests pinning the live shapes: heartbeat-only streams must not trap
    query(), LogEntry camelCase fields must populate, and the country catalog
    must stay a map.

Changed

  • composer phpstan now runs with --memory-limit=1G: PHPStan crashed on the
    default 128M CLI limit.

Upgrading from 1.0.0: read the breaking changes above first. Most of the
removed symbols described endpoints or fields the API never served, so code
using them was already failing at runtime, but Deployment::$author and
ProductsResource::countries() compile today and will not after upgrading.

Full changelog: v1.0.0...v2.0.0