Skip to content

Sync the locked contract and add fleet membership methods (1.2.0) - #10

Merged
roncodes merged 3 commits into
mainfrom
release/v1.2.0
Sep 7, 2026
Merged

Sync the locked contract and add fleet membership methods (1.2.0)#10
roncodes merged 3 commits into
mainfrom
release/v1.2.0

Conversation

@roncodes

@roncodes roncodes commented Sep 7, 2026

Copy link
Copy Markdown
Member

Summary

Syncs the locked contract to Postman 9b59bef (the merged fleetbase/postman#59) and adds the fleet membership endpoints from the Fleet-Ops public Fleet resource expansion.

This also unblocks the fleetbase.io API reference build, which currently fails on main: the docs generator requires a PHP example for every request in the collection, and the catalog was still the 220-request one.

The 21 new requests are not 21 new operations

Four are:

Method Route
assignDriverToFleet POST /fleets/:id/drivers/:driver
assignVehicleToFleet POST /fleets/:id/vehicles/:vehicle
removeDriverFromFleet DELETE /fleets/:id/drivers/:driver
removeVehicleFromFleet DELETE /fleets/:id/vehicles/:vehicle

The other seventeen are scenarios on endpoints already covered: retry an assignment to show it is idempotent, create a driver without contact details to show those are optional, send an empty relationship to show it is ignored.

The generator minted a method per request, so regenerating naively would have published createOrderWithEmptyRelationships, removeDriverFromFleetAgain, expandVehicleScalar, and updateDriverUnchangedContact — and would have put createFleetDriver and deleteFleetDriver on FleetService while they post and delete against /drivers, purely because the requests live in the Fleets folder. The compatibility gates make a published name permanent, so that is not a cost that can be walked back.

contracts/sdk-variants.json records which requests are scenarios and which operation each exercises, with a rationale per entry. A scenario still appears in the manifest, still appears in the API reference, and is still driven by the contract suite — through the canonical method, which is the claim worth testing: the scenario is reachable with the API the SDK already offers.

The examples improve as a result. Expand a Vehicle now documents what a caller actually writes:

$result = $fleetbase->vehicles->retrieveVehicle(
    $vehicleId,
    ['with' => ['vendor', 'not_a_relation']]
);

Adding an entry to the variant map never removes an existing method; it only declines to mint a new one. The map is validated in both directions — a stale entry that names no request fails generation, as does a variant pointing at another variant.

Two contract-generation bugs

Both are reachable by any request that repeats a query key, and both were latent before this sync:

  • normalizeParameters kept only the last value of a repeated parameter, so ?with[]=vendor&with[]=not_a_relation recorded a single with[] and silently dropped vendor. Repeats now collect into a list under the base name.
  • The manifest recorded the full url including an inline query string while the fixture recorded the query separately, so such a request carried its query twice. The manifest now records the path alone — which is what the other 239 requests already did.

ApiExamplesTest pinned its expected count as a literal, which is how it came to disagree with the lock. It now reads expected_requests from contracts/contract-lock.json.

Validation

All run locally on this branch:

  • composer lint — 158 files, no syntax errors
  • composer format:check — 0 of 158 files need fixing
  • phpstan (at --memory-limit=1G; the default 128M is not enough for this codebase) — no errors
  • composer test:unit — 35 tests, 4244 assertions, all passing
  • composer contract:check --require-complete — 241 requests, 241 mapped, 0 exceptions, 0 unmapped
  • composer contract:generated — regenerates to exactly the committed artifacts
  • composer api:compatibility — compatible with the 1.0.2, 1.0.3, and 1.1.0 baselines

Not run: the mutation score. The release checklist asks for it to be measured and recorded for the candidate before publication, and I left that line as a task rather than carrying forward the 1.1.2 figure.

Release

Version bumped to 1.2.0 — additive surface, no removals or renames. Merging this branch starts the release workflow and derives 1.2.0 from the branch name.

Coordinate with fleetbase/fleetbase.io#97, which consumes contracts/php-sdk-examples.json from this branch. Land this first so the documented methods exist in a published version.

Postman 9b59bef grows the locked collection from 220 to 241 requests,
covering the public Fleet resource expansion in Fleet-Ops. Four of the new
requests are genuinely new operations and become methods:
assignDriverToFleet, assignVehicleToFleet, removeDriverFromFleet, and
removeVehicleFromFleet.

The other seventeen are scenarios on endpoints the SDK already exposes. They
retry an assignment to show it is idempotent, create a driver without contact
details to show those are optional, or send an empty relationship to show it
is ignored. The generator mapped every request to a method of its own, so
those would have been published as createOrderWithEmptyRelationships,
removeDriverFromFleetAgain and the like — and createFleetDriver would have
landed on FleetService while posting to /drivers, because the request happens
to live in the Fleets folder. The compatibility gates make a published name
permanent, so this is not a cost that can be undone later.

contracts/sdk-variants.json now records which requests are scenarios and which
operation each one exercises. A scenario is still listed in the manifest and
still driven by the contract suite, through the canonical method — which is
the claim worth testing: this scenario is reachable with the API the SDK
already offers. Adding an entry to the map removes nothing that exists; it
only declines to mint something new.

Two contract-generation bugs surfaced while wiring this up, both reachable by
any request that repeats a query key:

- normalizeParameters kept only the last value of a repeated parameter, so
  ?with[]=vendor&with[]=not_a_relation recorded a single `with[]` and silently
  dropped `vendor`. Repeats now collect into a list under the base name.
- The manifest recorded the whole url including any inline query string, while
  the fixture recorded the query separately, so a request that inlined its
  query carried it twice. The manifest now records the path alone, which is
  what the other 239 requests already did.

ApiExamplesTest read its expected count from a literal, which is how it came
to disagree with the lock. It now reads expected_requests from the contract
lock instead.
The contract workflow pinned FLEETBASE_STACK_REF, POSTMAN_REF and the two
submodule assertions as literals, duplicating contracts/contract-lock.json.
Syncing the contract updated the lock and left the workflow behind, so the run
booted the stack whose fleetops predates the fleet membership endpoints and
fetched the 220-request collection, then checked it against a 241-request
manifest. Postman failed with "Unable to find request or folder" and the
evidence check reported 24/241 invoked.

The gate job now reads the four refs out of the lock and publishes them as
outputs, and the contract job derives its environment and its "Prove locked
package refs" assertion from those. The lock is the source of truth its name
claims to be, and this class of drift cannot recur.

The values this moves to were verified rather than assumed: the stack at
699ceffe carries fleetops e3b8cf98 (v0.6.63, which has the membership
endpoints) and core-api b7691c06 unchanged, and the published image the run
pulls, fleetbase/fleetbase-api:latest, is v0.7.57 built from that same
fleetops commit. The previous image, v0.7.56, still carried a9131dae, so the
old pin could not have passed no matter which collection it fetched.
@roncodes
roncodes merged commit 3e8cf88 into main Sep 7, 2026
30 checks passed
@roncodes
roncodes deleted the release/v1.2.0 branch September 7, 2026 02:44
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