Skip to content

Document and test expanded Fleet resource APIs - #59

Merged
roncodes merged 4 commits into
mainfrom
feature/public-fleet-resource-api-contracts
Sep 6, 2026
Merged

Document and test expanded Fleet resource APIs#59
roncodes merged 4 commits into
mainfrom
feature/public-fleet-resource-api-contracts

Conversation

@roncodes

@roncodes roncodes commented Sep 4, 2026

Copy link
Copy Markdown
Member

Summary

Documents and asserts the public Fleet, Vehicle and Driver contracts as
implemented in fleetbase/fleetops#311.

This revision corrects the relationship design from the first one. The earlier
commit documented and asserted a public ID under the object key, which would
have made a property an object on one call and a string on another. The SDK
stores what the API returns verbatim, so every consumer that dereferences such a
property breaks — Navigator interpolates driver.user straight into a socket
channel name, where an object becomes user.[object Object].

Implementation PR: fleetbase/fleetops#311

Type of Change

  • Feature
  • Documentation
  • Test

Implementation Notes

Relationships are two keys

<name>_id is the public identifier and is always present. <name> is the
nested object, returned only when it was asked for through ?with=, and never a
string. Object definitions, examples and assertions all say so, and an automated
sweep confirms no Fleet, Vehicle or Driver response example renders a
relationship key as a string.

  • Vehicle gains driver_id, vendor_id, category_id, warranty_id,
    photo_id, and fuel_card_number — which the object definition never carried
    even though the API returned it. photo_id is documented as the file
    reference, with photo_url as the convenience URL and avatar_url called out
    separately as a display asset.
  • Fleet gains service_area_id, zone_id, vendor_id, parent_fleet_id,
    photo_id.
  • Driver keeps user, company, company_name, vehicle, vendor and
    current_job in every example, alongside vehicle_id, vendor_id and
    job_id. user is documented as a string that is never expanded, with the
    reason.

Expansion

?with=vendor, ?with[]=vendor, ?with=vendor,driver and the expand alias
are documented, and two new requests prove the array and scalar spellings
equivalent rather than assuming it:

Request Order Asserts
Expand a Vehicle 3100 with[]=vendor adds the object, vendor_id unchanged and equal to vendor.id, an unsupported name is ignored
Expand a Vehicle Scalar 3200 with=vendor produces the identical result

Identifier lookup

internal_id and public_id are documented as exact on the public API,
with the console's partial search called out as internal-only — an importer that
keys on VEH-10 must not match VEH-100.

Assertions

Vehicle create and Driver update now drive their assertions off the request
body, so a field cannot be added to a request without an assertion following it.
Specifically asserted: purchased_at, the coordinates-to-location
canonicalisation, vehicle status normalisation, driver email, phone and the
new timezone, relationship public-ID prefixes, and the object/identifier
invariants under expansion. The uuid-leak checks became a sweep over every
response key rather than a fixed list.

Response shapes are the real ones — bare objects and bare arrays, no invented
{data: ...} wrapper. 200/201 are accepted only where the implementation
permits either.

Validation

  • Lint
$ npm run postman:lint
Validated 5 Postman Native Git collections and 3 environments.
Linting Fleetbase API...            No issues found. Scanned: 214 | Errors: 0 | Warnings: 0
Linting Fleetbase Core API...       No issues found. Scanned: 24  | Errors: 0 | Warnings: 0
Linting Fleetbase Integrated Vendor Flow... No issues found. Scanned: 7 | Errors: 0 | Warnings: 0
Linting Fleetbase Ledger API...     No issues found. Scanned: 8   | Errors: 0 | Warnings: 0
Linting Fleetbase Storefront API... No issues found. Scanned: 62  | Errors: 0 | Warnings: 0

Both scripts/validate-collections.js and the Postman Native Git schema lint ran
(Postman CLI 1.46.0).

Live run — passed. fleetbase/fleetops#311 temporarily pinned its contract job
to this branch's commit so the two were validated together. Run
33953882575,
fleetops a249e7df × postman 91e72fe, against a booted stack carrying the
Fleet-Ops branch:

requests      237 executed, 0 failed
test-scripts   83 executed, 0 failed
assertions    314 executed, 0 failed

The pin has been reverted on that PR. Running this collection against a
deployment that does not carry #311 will fail, because the endpoints it asserts
do not exist there.

The first pinned run failed and was worth running: it caught a 500 on
GET /v1/vehicles/{id}?with[]=… in the implementation, and two assertions here
that compared the response against pm.request.body.raw — which re-resolves
{{$randomEmail}} every time it is read, so they could only ever fail. Both are
fixed.

Documentation Impact

  • Documentation needed but not included

API Reference Impact

  • Updated fleetbase/postman

If the fleetbase.io API reference is generated from this repository it needs
regeneration once both PRs land. No change was made to fleetbase/fleetbase.io.

Risk

Specification and tests only. The Fleets folder creates and deletes its own
vehicle and driver because the Vehicles and Drivers folders delete theirs before
it runs. The driver authentication and password requests are untouched:
driver_identity, driver_password and driver_phone are still not written by
anything here.

Confirmation

No credentials, API keys, tokens or customer data. All identifiers in examples
are illustrative. Not merged.

Brings the Fleetbase API collection in line with the expanded public Fleet,
Vehicle and Driver contracts, and adds request-specific response assertions so
a collection run proves the behaviour rather than only that a 2xx came back.

Fleets
- Create/Update accept name, color, task, status and the service_area, zone,
  vendor and parent_fleet relationships as public IDs.
- New "Create a Subfleet" request covering the parent_fleet hierarchy, with
  422 self-parent and 404 cross-company examples.
- Four new membership requests, plus a repeat of each, covering assignment,
  idempotent reassignment, removal and no-op re-removal for both vehicles and
  drivers, with a single stable response shape.
- Relationships documented as public IDs, with the nested-object shape kept for
  ?with= requests.
- Query parameters documented: name, public_id, status, task, parent_fleet,
  parents_only, vendor, service_area, zone.

Vehicles
- Every safe business field the vehicle record holds is now documented on
  create and update.
- The status enum is reconciled with the implementation: nineteen values were
  accepted while three were documented.
- Query parameters documented, including internal_id, which an importer keys on.

Drivers
- email and phone documented as optional, with a dedicated "Create an
  Operational Driver" request for a record with neither.
- Identity, operational, structured and orchestrator fields documented.
- password removed from the update contract, which the API does not accept.
- Query parameters documented, including internal_id and licence number.

The Vehicles and Drivers folders delete their own resources before the Fleets
folder runs, so the membership requests create and clean up a vehicle and a
driver of their own rather than reaching for ids that no longer resolve.

Validation: npm run postman:lint — 0 errors, 0 warnings across all five
collections. Live collection execution is pending deployment of the Fleet-Ops
branch.

No credentials, real API keys or customer data are included.
roncodes and others added 3 commits September 5, 2026 15:17
…okup

Follows the Fleet-Ops contract correction: a relationship is two separate keys,
never one key with two types.

Relationship shape
- `<name>_id` is the public identifier and is always present. `<name>` is the
  nested object. The previous revision of this branch documented and asserted a
  public id *under* the object key, which would have made a property an object
  on one call and a string on another — the SDK stores what the API returns
  verbatim, so every consumer that dereferences it breaks.
- Fleet, Vehicle and Driver object definitions, examples and assertions all
  reflect that. Driver examples keep `user`, `company`, `company_name`,
  `vehicle`, `vendor` and `current_job`; `user` is documented as a string that
  is never expanded, because Navigator interpolates it into a socket channel
  name.
- Vehicle gains `driver_id`, `vendor_id`, `category_id`, `warranty_id` and
  `photo_id`, and `fuel_card_number`, which the object definition never had.

Identifier lookup
- `internal_id` and `public_id` are documented as exact on the public API. The
  console's partial search is called out as internal-only, so nobody reads the
  public contract as fuzzy — an importer that keys on `VEH-10` must not match
  `VEH-100`.

Expansion
- `?with=vendor`, `?with[]=vendor`, `?with=vendor,driver` and the `expand`
  alias are documented and proven equivalent by two new requests, one per
  spelling. Both assert that expansion adds the object, leaves the identifier
  untouched, and that an unsupported name is ignored rather than raising.

Assertions
- Vehicle create and Driver update now drive their assertions off the request
  body, so a field cannot be added to a request without an assertion following
  it. `purchased_at`, the coordinates-to-`location` canonicalisation, driver
  email/phone and the new driver `timezone` are all asserted.
- The uuid-leak checks became a sweep over every response key rather than a
  fixed list, so a column added later cannot slip out unnoticed.

Validation: npm run postman:lint — 0 errors, 0 warnings across all five
collections. Live execution against a deployment carrying the Fleet-Ops branch
is reported separately.
… them

The cross-repository contract run failed two driver assertions comparing the
response's email against pm.request.body.raw. The body carries
{{$randomEmail}} and {{$randomPhoneNumber}}, which Postman resolves afresh
every time that body is read — so the comparison was against a different address
than the one actually sent, and the assertion could only ever fail.

Both now assert shape rather than a value that cannot be recovered after the
fact, and a new 'Update a Driver Unchanged Contact' request resends the address
and number the response reported. That is the case the update-time uniqueness
rule has to let through: without its ignore-own-user clause a driver could not
resend its own address.
@roncodes
roncodes merged commit 9b59bef into main Sep 6, 2026
1 check passed
@roncodes
roncodes deleted the feature/public-fleet-resource-api-contracts branch September 6, 2026 04:47
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