Skip to content

Repository files navigation

ovh-api

CI regenerate docs License: MIT

A generated Ruby transport gem for the entire OVH REST API (all 70 top-level resources), produced from OVH's published schemas.

ovh-api is the thin, machine-generated HTTP layer. Business ergonomics and quirk absorption belong in a hand-written wrapper on top (ovh-client, à la dolibarr-client over dolibarr-api). OVH's request-signing auth is not baked in here — it is injected as a middleware through the generator's auth seam (see Authentication below).

How it is built

OVH does not publish OpenAPI. Its API is described in legacy Swagger 1.2 (per-resource API Declarations, no swaggerVersion, no security definitions), which openapi-generator cannot ingest. The pipeline bridges that gap:

versions/raw/*.json         pinned OVH 1.2 declarations (70 resources)
  └─ scripts/convert.mjs
       ├─ union-merge colliding models   OVH redefines shared types (service.*, order.*, …)
       │                                 per product; enums → union of values, structs →
       │                                 union of properties (all optional)
       ├─ swagger-converter              1.2 → 2.0, merges all 70 resources into one doc
       ├─ 3 structural fixups
       │    • dangling $refs             OVH scalar types (password, ipBlock, …) declared
       │                                 as undefined models; array aliases (Foo[]); maps
       │    • multi-body params          1.2 emits one `body` param per POST field; 2.0
       │                                 allows only one → merged into a single object
       │    • missing path params        `{id}` in the template but undeclared (e.g.
       │                                 /telephony …/{queueId}/agent)
       └─ swagger2openapi                2.0 → 3.0
  └─ versions/ovh.oas3.json   → fed to openapi-generator (-g ruby-nextgen)

The last full run: 70 resources → 4728 paths, 5268 schemas, OpenAPI 3.0.0, openapi-generator validate clean (0 errors; only harmless "unused model" warnings).

Two models remain mapped to a free-form object (lossy but harmless): T (a generic placeholder) and coreTypes.AccountId:string.

Commands (mise)

mise run fetch      # refresh versions/raw/*.json from api.ovh.com (overwrites pinned schemas)
mise run convert    # versions/raw → versions/ovh.oas3.json
mise run generate   # generate the gem into lib/** (-g ruby-nextgen)
mise run build      # convert + generate + format
mise run dev:spec   # run the generated spec suite

versions/raw/*.json are committed so the build is reproducible without hitting OVH. Run mise run fetch only to deliberately refresh the pinned schemas.

What is not committed

Three pipeline artifacts are git-ignored, so a fresh clone needs mise run convert before mise run generate (the latter fails fast with a message if the spec is missing):

Artifact Why it is ignored
versions/ovh.oas3.json Intermediate spec (~5 MB), regenerated deterministically from versions/raw/*.json. Committing it would add unreadable multi-MB diffs on every re-pin, duplicating information the committed inputs already carry.
scripts/node_modules/ Installed dependencies.
scripts/package-lock.json Deliberate: see the caveat below.

Reproducibility caveat: scripts/package.json pins the two converters exactly (swagger-converter 1.5.1, swagger2openapi 2.9.4), but with no committed lockfile their transitive dependencies float. Two npm install runs at different dates can therefore produce a byte-different versions/ovh.oas3.json with no versioned file having changed. This does not affect CI — it lints and tests the committed lib/** and never regenerates — but a regeneration is only reproducible to the extent that npm resolves the same transitive tree. Commit scripts/package-lock.json if that guarantee is ever needed.

Generator dependency

The gem is generated with ruby-nextgen, which ships in the published openapi-generator release pinned as OPENAPI_GENERATOR_VERSION in mise.toml (currently 7.24.0). No fork and no local Maven build are involved.

Locally, install the generator from Homebrew:

brew install openapi-generator

CI has no Homebrew formula, so regenerate.yml downloads the pinned jar from Maven Central and exports OPENAPI_GENERATOR_CMD=java -jar …. Either way mise run generate cross-checks the generator's reported version against the pin and fails loudly on a mismatch, rather than silently emitting different code. To move to a newer generator, bump the pin — that is the only place the version lives.

Authentication (request signing)

OVH signs each request: headers X-Ovh-Application, X-Ovh-Consumer, X-Ovh-Timestamp, and X-Ovh-Signature = $1$ + SHA1(`app_secret + consumer_key + METHOD + full_url + body

  • timestamp). This is a per-request signature, **not** an OpenAPI security scheme, so it is injected through the ruby-nextgen` middleware seam rather than generated:
require "digest/sha1"

class OvhSignature < Faraday::Middleware
  def initialize(app, app_key:, app_secret:, consumer_key:)
    super(app)
    @app_key, @app_secret, @consumer_key = app_key, app_secret, consumer_key
  end

  def on_request(env)
    ts  = Time.now.to_i.to_s
    sig = "$1$" + Digest::SHA1.hexdigest(
      [@app_secret, @consumer_key, env.method.to_s.upcase, env.url.to_s, env.body.to_s, ts].join("+"))
    env.request_headers.merge!(
      "X-Ovh-Application" => @app_key, "X-Ovh-Consumer" => @consumer_key,
      "X-Ovh-Timestamp"   => ts,       "X-Ovh-Signature" => sig)
  end
end

client = Ovh::Api::Client.new do |config|
  config.use(OvhSignature, app_key: ENV["OVH_APP_KEY"], app_secret: ENV["OVH_APP_SECRET"],
             consumer_key: ENV["OVH_CONSUMER_KEY"])
end

Layout

versions/raw/       pinned OVH 1.2 schemas (source of truth, committed)
versions/resources.txt   the 70 top-level resource paths
scripts/convert.mjs conversion pipeline (1.2 → OpenAPI 3.0)
mise.toml           fetch / convert / generate / build tasks
lib/                generated gem — DO NOT edit by hand

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages