OpenAPI 3.1 coverage
What bin/altair openapi:import (OpenAPI → Altair) and spec:emit-openapi
(Altair → OpenAPI) actually do with each OpenAPI feature. This is the honest,
evidence-based map — kept current as #214
closes the gaps.
Standard. Map everything representable; surface (warn/error) everything else — never silently drop; emit spec-compliant OpenAPI; keep the round-trip stable. Literal 100% fidelity into generated code isn’t a goal — some features (
callbacks,links,oneOfpolymorphism, free-formadditionalProperties) have no clean Altair representation.
Import (OpenAPI → Altair spec)
Section titled “Import (OpenAPI → Altair spec)”Mapped
Section titled “Mapped”- Paths + operations;
operationId(or synthesised),summary. - Resource naming (Phase 5) — RPC-style path leaves (
findByStatus,uploadImage,login) are recognised as actions, not resources:GET /pet/findByStatusderives thePetnamespace (App\Pet\FindPetsByStatus), not a mangledFindByStatu. The nearest noun segment is the resource. - Parameters — path / query / header / cookie become inputs tagged with their
in:location (with their declared type +required; enums →in:rule). A path param declared withschema: {type: integer}imports asint, notstring. Phase 2. - Request + response bodies in
application/json. When a request body has noapplication/json, its schema is read from the first content type carrying an object/array schema (multipart/form-data, x-www-form-urlencoded) — the body is normalized, not dropped (Phase 4a). Responses fall back to the first content type with any schema. On export the body always re-emits asapplication/json, so the wire content type is normalized while the body structure round-trips. - Schema types:
object(incl. nested objects),array, arrays of objects, top-level array bodies, scalars (integer→int,number→float,boolean→bool,string),enum→in:rule. - Validation constraints ↔ rules (Phase 3):
format(email/uri/ip/date-time)→email/url/ip/datetime;minLength/maxLength→min/max;minimum/maximum→min/max;pattern→regex:. Round-trips (the forward emitter writes the inverse). - Internal
$ref(#/components/schemas/<Name>),properties+required. - External relative-file
$ref(Phase 4e) — a$refinto a sibling file (./schemas/pet.yaml#/Pet) is bundled: the target schema (and its own nested/internal refs) is inlined intocomponents/schemasand the ref rewritten to internal, so multi-file specs import instead of failing. Bounded (file count, per-file size, ref-chase depth; cycles dedupe). Remote refs are never fetched and a ref escaping the document’s directory is rejected — both are warned and left unresolved. allOfcomposition (Phase 4b) — subschemas are resolved ($refs included) and their properties merged into one object; a property required in any subschema is required in the merge. Altair has no representation for composition, so the relationship is flattened (warned) while the data is preserved. A subschema that does not resolve to an object is unmappable.x-altair-*extensions (domain/persistence/queue/idempotency/webhook round-trip).
Surfaced as warnings (imported, but reported — not silent) — Phase 1
Section titled “Surfaced as warnings (imported, but reported — not silent) — Phase 1”openapi:import warns about each of these in its receipt (warnings[]) and human output:
- parameter
$ref(not resolved). - non-
application/jsonrequest bodies whose schema is read — when JSON is present the other representations are ignored; when it is absent an object/array body is normalized (Phase 4a). Either way the importer says which content type(s) it read or ignored. - binary/scalar-only request bodies (
application/octet-stream,text/plain) — no named-field representation, so still dropped (warned). - non-
application/jsonresponses whose schema is read — when a response has noapplication/json, its schema is normalized from the first content type carrying one, and the importer reports the normalization (Phase 4a). Whenapplication/jsonis present the other representations are alternative views, not a loss, so they are not warned. - requestBody
$ref(body dropped). allOfflattening (Phase 4b) — reported once per named component that uses it (`components.schemas.<Name>` uses allOf) and per inline body/response that uses it, since the composition relationship is not preserved.oneOf/anyOf(Phase 4c) — a union has no Altair representation; reported per component / inline body / response. AoneOf/anyOfbody stays unmappable (skipped with--skip-unmappable); a union response surfaces asmixed.additionalProperties(Phase 4d) — an open-ended map; reported per component / inline body / response. Declaredpropertiesstill map; only the open-key capability is dropped. An explicitadditionalProperties: false(closed object) is not warned.- operation + global
securityandcomponents.securitySchemes— surfaced by design (Phase 5): Altair has no auth/middleware spec construct to generate into, so the requirement is reported (so you wire auth yourself) rather than silently dropped. This is a deliberate non-goal, likeoneOf— not a pending gap. servers,webhooks(3.1),callbacks, path-item$ref.
Surfaced as errors / skips (fail, or --skip-unmappable)
Section titled “Surfaced as errors / skips (fail, or --skip-unmappable)”- Remote (
http(s)://)$ref, or a file$refescaping the document directory — warned by the bundler and left unresolved, so the mapper surfaces them as unmappable (never fetched/read). oneOf/anyOfin a request body; recursive$ref; a bare scalar body (incl. a normalized non-JSON body that turns out to be a scalar); anallOfwhose subschema is not an object.
Not yet mapped or warned (later phases)
Section titled “Not yet mapped or warned (later phases)”- Remaining constraints:
multipleOf,min/maxItems,uniqueItems(no Altair rule yet). - requestBody
requiredflag;additionalProperties,const,discriminator,not,prefixItems,nullable;deprecated,default,example(s),title/description; responseheaders/links. - A bare
requiredsibling on anallOfnode (marking inherited properties required without re-declaring them) —requiredinside anallOfsubschema is honored; a top-levelrequiredalongsideallOfwith no siblingpropertiesis not yet applied (the flat field model has no slot for a required name without a property).
Export (Altair spec → OpenAPI)
Section titled “Export (Altair spec → OpenAPI)”spec:emit-openapi emits operations, responses from output:, an
application/json request body from body inputs, OpenAPI parameters for
inputs tagged in: path|query|header|cookie (Phase 2), and schema
constraints from validation rules — email→format, min:3→minLength,
in:→enum, regex→pattern (Phase 3). security and servers are not
emitted — there is no Altair spec construct that carries them (see the security
note above).
Roadmap
Section titled “Roadmap”See #214 for the phased plan
(stop silent loss → parameters → validation fidelity → content & composition →
security & misc). The epic is complete. Phases 1–3 (stop silent loss,
parameters, validation), Phase 4 (4a non-JSON bodies, 4b allOf, 4c/4d
oneOf/anyOf + additionalProperties, 4e external-$ref bundling), and
Phase 5 (resource-naming fixes; path-param types; security surfaced by design)
have all landed. Remaining items are deliberate non-goals (composition unions,
auth middleware) or low-value constraints, each surfaced rather than dropped.