Architecture¶
The conventions shared with aioscaleway are in the Business Agile conventions
(backend/08-python-api-clients.md, draft in
/opt/businessagile/libraries/conventions-clients-api.md). This file covers
what is specific to OVHcloud.
Two layers¶
- Operations (
src/aioovh/_operations/v1/<api>/,.../v2/<api>/), private: one typed method per operation of each schema, with its models, generated byscripts/operations.py. Method names derive from the path and the HTTP method, since the schemas have no operation identifiers. - Objects (
src/aioovh/<area>/), public: business areas, collections, references and loaded objects, generated byscripts/objects.pyfrom the operations andmapping.yaml.
The hand-written runtime is _client.py (signature, clock offset, OAuth2,
retries), _objects.py (collections, references, binding, bounded loading),
_root.py (OVH), _settings.py, _endpoints.py, _errors.py,
_models.py and _enums.py.
The mapping¶
mapping.yaml lists every API root, as a path:
- a collection root (
/me/bill: {namespace: billing, name: bills}) starts a collection namednamein the namespace; - an area (
/me: {namespace: account, area: true}) is not a resource: its sub-paths become collections and methods of the namespace, and aGETon the area itself is whatawait ovh.accountreturns; - a scope (
scope: {serviceName: project}) turns a path parameter of the root into a keyword with a default from the settings: the Public Cloud project; - a prefix puts a word in front of an area's names when two areas of a
namespace would clash (
managedfor the v2 managed databases next to the 1.0 ones,public_cloudfor the v2 Public Cloud areas, in BETA).
The longest matching root wins, so /me/bill goes to billing while the rest
of /me goes to account. A root left out of the mapping stops the
generator.
Placement of each operation¶
Under a root, a static segment followed by a parameter is a resource
(/me/bill/{billId}). Then:
- on the item path:
GETloads,PUTisupdate,DELETEisdelete; a secondGET(from the other API version) isdetails; - on the collection path: a
GETreturning a list is the iteration,POSTiscreate; - a static sub-path with items is a sub-collection (
bill.details); one without items is a method named after it (service_infos(),update_service_infos(),reboot()), or an iterable listing when it returns a list; - deprecated operations stay in the operations layer only.
A GET that needs more than the identifier (a required query parameter) is a
method, not the load.
Lists of identifiers¶
Most 1.0 lists return identifiers. Iterating a collection loads each object,
max_concurrency at a time in a task group that ends before the next batch:
no task outlives the async for. ids() skips the loading.
Types and dates¶
datetime fields are whenever.Instant, date fields whenever.Date; a
datetime query filter also accepts a Date. Path parameters typed as an
enum, a date or an integer are converted from the key, which holds strings.
Every model is rebuilt at the end of its module, so that loaded classes in
another module inherit resolved fields.
Each schema is self-contained, so shared models such as order.Price exist
once per schema that uses them; an area's types module prefixes a name with
its API (MeOrderPrice) when two schemas of the area define it.
Requests¶
Each request is built, then signed on its exact URL and body; each retry is
signed again. The clock offset is read once from /auth/time. With
client_id and client_secret, an OAuth2 token is used instead and renewed
30 seconds before it expires.
Retries¶
Idempotent methods are retried on 429, 502, 503, 504 and transport
errors; a POST only on 429, 503 or a connection that never opened.