OpenAPI 2.0 (Swagger)¶
Parses a Swagger 2.0 specification into the Vayu draft model. Swagger 2.0, like OpenAPI 3.0, is a specification document, not a request log - it describes endpoints, parameters, and schemas but carries no concrete values. The parser therefore emits synthetic request stubs: a {{baseUrl}} built from schemes/host/basePath, query and header params carrying a declared default when there is one (and importing disabled when there is not - see Parameter values & enabled state), and a body sampled from the in: "body" parameter schema. Users fill in real values after import.
The document is not thrown away once it has been read. The collection it
produces is bound to it: the spec is stored verbatim (with the URL it was
fetched from, when there was one), and every request records which operation it
is - operationId, method, and the templated path. See
OpenAPI Collections for what the binding is for and what the
collection's Spec tab does with it.
The requests this parser builds are mirrored engine-side as well (issue #865,
core::spec_request_drafts_of), for the reason and under the conformance
fixture written out in OpenAPI 3.0 - both
dialects are read by that one port, so a 2.0 rule changed here changes there too.
- Source:
engine/src/core/import_document.cpp + openapi_drafts.cpp - Exports:
The parse moved engine-side (issue #877). Every rule on this page is the same rule it always was - the corpus in
engine/tests/fixtures/import-conformance.jsonwas recorded from the parser this replaced and is asserted against on every build - it is simply read byengine/src/core/import_document.cppnow, behindPOST /import/parse, rather than in the renderer. Module names in C++ style below name that file's functions; the app holds no parser.
| Symbol | formatName |
formatKey |
|---|---|---|
OpenApiV2Parser (class, implements ImportParser) |
OpenAPI 2.0 (Swagger) |
openapi-v2 |
swaggerSchemeToAuth (helper) |
- | - |
OpenApiV2Parser exposes formatName / formatKey as readonly fields and implements detect + parse from ./types.
Detection¶
The top-level swagger field must be the string "2.0" or the number 2 (issue #719). The string is what the specification says and what every JSON export writes - JSON has no other option, since the key is quoted or the file is not valid. Hand-written YAML routinely leaves it unquoted, and js-yaml then loads 2.0 as the number 2, so a string comparison alone reported an ordinary Swagger file as "Unrecognised format". The v3 side never had this problem for a reason that does not generalise: 3.0.3 has two dots, so it stays a string whatever the quoting. Only detection needs the widening - nothing in parse reads the field, and the document is stored as the bytes it arrived as, so there is no normalized copy to keep in step. OpenAPI 3.x (the openapi field, no swagger) is handled by a separate parser - see OpenAPI v3. The factory (core::parse_import) parses the raw text once (JSON, then YAML fallback) and runs each parser's detect in registration order.
Tree structure¶
The spec maps to a single root collection, with operations grouped into child collections by their first tag - or, for an operation that declares no tag, by the first meaningful segment of its path (issue #710).
- Root collection ← the whole spec (named from
info.title). It directly holds: requests: every operation that has neither a tag nor a groupable path.children: one child collection per distinct folder name, in first-encounter order.- Child collections ← created lazily by
OperationFolders(openapi_drafts.cpp), the first time a name is seen. A tag-named folder takes its description from the matching entry in the top-leveltags[]array (if any); a path-named folder has none.
Iteration order: parse loops over spec.paths entries, and for each path item over the fixed HTTP_METHODS list (get, post, put, patch, delete, head, options). For each present operation it calls buildSwaggerOp(...), then routes by tag:
const tag = op.tags?.[0]; // ONLY the first tag is used
const name = tag ?? pathFolderName(path); // the fallback, per operation
if (name) folders.get(name).requests.push(req);
else rootRequests.push(req);
Multi-tag operations: only op.tags[0] is consulted. An operation with tags: ["a", "b"] lands solely in the a child collection; b is ignored (no duplication, no extra folder).
Untagged operations take the same path fallback the v3 parser does, from the same OperationFolders - the rule, its version/api/{template} skips and meta.folderStrategy are written out once, in OpenAPI 3.0.
Unlike v3 (which has a dedicated buildBody helper), v2 builds the root collection inline in parse and delegates only the per-operation draft to buildSwaggerOp. $ref resolution comes from walk::resolve_ref (openapi_drafts.cpp), the same one the v3 parser uses - both had built it by hand, identically, until issue #649.
References naming other files (./definitions/pet.yaml#/Pet) are resolved before this parser runs, by ref-bundler.ts; what could not be reached is counted as an external_ref SkippedItem. The rules are the same for both OpenAPI parsers and are written out in OpenAPI 3.0 and OpenAPI Collections.
Field mapping¶
Collection (root)¶
Built inline in parse.
| Swagger | Vayu CollectionDraft |
Notes |
|---|---|---|
info.title |
name |
fallback "Imported API" |
info.description |
description |
fallback "" |
schemes + host + basePath |
variables.baseUrl |
only added when host is present: { baseUrl: { value: baseUrl, enabled: true } }; otherwise variables is {}. See Base URL. |
security / securityDefinitions |
auth |
via the picked primary scheme + swaggerSchemeToAuth (see Auth). Collections never inherit. |
| (none) | preRequestScript / postRequestScript |
always "" |
| tag and path groups | children |
folders.children() |
| operations with neither | requests |
Collection (per folder)¶
Built by OperationFolders (openapi_drafts.cpp), shared with the v3 parser.
| Swagger | Vayu CollectionDraft |
Notes |
|---|---|---|
tag (the string) |
name |
|
tags[].description where tags[].name === tag |
description |
fallback "" |
| (none) | variables |
always {} (baseUrl lives only on the root) |
| (none) | auth |
always { mode: "none" } - tag collections do not carry security |
| (none) | children |
always [] (tags are flat; no nesting) |
| (none) | preRequestScript / postRequestScript |
always "" |
Request (per operation)¶
Built by buildSwaggerOp(method, path, op, spec, resolveRef, pathParams, tally, specOperation).
| Swagger | Vayu RequestDraft |
Notes |
|---|---|---|
op.summary → op.operationId → "{METHOD} {path}" |
name |
precedence in that order; final fallback uses upper-cased method + raw path, e.g. "GET /users/{id}" |
op.description |
description |
fallback "" |
| HTTP method | method |
method.toUpperCase() (e.g. get → GET), cast to HttpMethod |
path |
url |
`{{baseUrl}}${normalize_path_templates(path)}` - always prefixed with {{baseUrl}}, even if no host was defined (see URL) |
parameter in: "query" |
params |
{ key: name, value, enabled, description? } via declared_param_row - description included only when present; value and enabled follow Parameter values & enabled state. Only the enabled rows are joined onto the url by parseImport - see The url/params invariant |
parameter in: "header" |
headers |
{ key: name, value, enabled } via the same declared_param_row - no description carried (the Headers table has no column for one); authorization and content-type headers are dropped (case-insensitive) since Vayu manages those |
parameter in: "body" |
body |
sampled via Sampler::sample; JSON vs text decided by consumes (see Parameters & body) |
parameter in: "formData" |
body |
collected into form fields; the encoding (x-www-form-urlencoded vs form-data) comes from consumes (see consumes → body mode) |
parameter in: "path" |
- | not emitted as params/headers; path params are represented in the URL via normalize_template_vars |
| (none) | auth |
always { mode: "inherit" } - auth is configured once at the collection level |
| (none) | preRequestScript / postRequestScript |
always "" |
op.responses |
examples |
via buildSwaggerExamples (see Documented responses); absent when nothing was representable |
op.operationId, method, path |
specOperation |
the operation this request is, recorded for sync. Claimed by the one walk, so an operationId this document declares twice is kept on the first operation only and the second is identified by method and path - the rule and its reason are written out in OpenAPI 3.0 |
Parameter resolution & merge. buildSwaggerOp concatenates path-item-level parameters (passed in as pathParams) with operation-level op.parameters, resolving any $ref entries via resolveRef. Each parameter is keyed by `${in}:${name}` in a Map (byKey), so an operation-level parameter overrides a path-level one with the same in+name (later writes win). Entries missing in or name after resolution are skipped.
Both lists go through the walk's parameters guard (openapi_drafts.cpp) first, shared with the v3 parser: a parameters value that is present but not an array (the missing-- YAML mistake) used to throw is not iterable and abort the whole file. It is now treated as empty and counted as a malformed_spec SkippedItem; an absent parameters is normal and counted as nothing.
Path items. Each spec.paths entry goes through walk::resolve_single_hop (openapi_drafts.cpp) before its methods are read, so a path item written as {"$ref": "..."} contributes its target's operations instead of vanishing (Swagger 2.0 allows a path-item ref; the resolver is generic, so any in-document pointer works). One hop only. A path item that is not an object, or whose $ref does not resolve to one, is counted as malformed_spec and skipped.
Documented responses¶
Saved example responses (issue #481), from the half of the spec that says what comes back. The 2.0 shape differs from v3 in where the payload lives - examples is keyed by MIME type and holds the value directly (no Example Object wrapper), and schema sits on the response itself rather than under a media type - so this parser supplies that half and shares everything else with v3 through response_example (openapi_drafts.cpp).
Swagger (op.responses[code]) |
Vayu ExampleDraft |
Notes |
|---|---|---|
| the key | status |
must be a three-digit 100-599 code |
description |
name |
"{code} - {description}", or the bare code |
examples[<media type>] → sampleSchema(schema) |
body |
documented example first, generated sample second - the same precedence this file already uses for a request body |
op.produces → spec.produces |
contentType, and a single Content-Type header |
a 2.0 response does not name its own media type; the JSON entry wins, and a spec that lists no produces at all is treated as application/json |
A response that documents no body still imports (204 is a real answer), and a key that is not a numeric status is skipped and counted: default as default_response, a wildcard or junk key as example_no_status - counted apart for the reason OpenAPI 3.0 gives.
Base URL construction¶
The base URL is assembled from three top-level spec fields and stored as the baseUrl collection variable on the root only:
const scheme = (spec.schemes?.[0] as string) ?? "https";
const basePath = spec.basePath && spec.basePath !== "/" ? spec.basePath : "";
const baseUrl = spec.host ? `${scheme}://${spec.host}${basePath}` : "";
- Scheme - the first entry of
schemes[]; ifschemesis absent/empty, defaults to"https". Additional schemes (http,ws,wss, …) are ignored - onlyschemes[0]is used. basePath- appended verbatim unless it is missing or exactly"/"(in which case it contributes nothing, avoiding a trailing//).host- required for a base URL. Ifhostis absent,baseUrlis"", nobaseUrlvariable is added (rootvariablesstays{}), and request URLs still carry the literal{{baseUrl}}prefix (resolving to empty at runtime).
Examples (host: "api.example.com"):
schemes |
basePath |
baseUrl |
|---|---|---|
["https"] |
"/v2" |
https://api.example.com/v2 |
["http", "https"] |
"/v2" |
http://api.example.com/v2 (first scheme only) |
| (absent) | "/" |
https://api.example.com (default scheme, / dropped) |
["https"] |
(absent) | https://api.example.com |
When set, the value is stored as variables.baseUrl = { value: baseUrl, enabled: true }.
URL & path parameters¶
- The request
urlis always`{{baseUrl}}${normalize_path_templates(path)}`.{{baseUrl}}is the Vayu collection variable described above (defined on the root collection). If the spec has nohost,baseUrlis absent from the root variables and{{baseUrl}}resolves to empty at runtime. - Swagger path templates
{param}are converted to Vayu{{param}}bynormalize_template_vars(path_template.cpp). WithpathTemplates(which only the OpenAPI/Swagger parsers pass) it rewrites single-brace{x}(identifier chars[\w$-]) to{{x}}, while leaving any existing{{...}}pairs intact. So/users/{userId}/posts/{postId}becomes/users/{{userId}}/posts/{{postId}}. Path parameters (in: "path") are not also emitted asparamsentries - they live only in the URL.
Parameters & body¶
Swagger 2.0 has no requestBody object (unlike v3). Request bodies are expressed as ordinary parameters with a special in value. buildSwaggerOp iterates the resolved, deduped parameters and dispatches on param.in:
param.in |
Effect | Detail |
|---|---|---|
query |
push to params |
declaredParamRow(name, param.default, param.required, description); description only when present (see Parameter values & enabled state) |
header |
push to headers |
declaredParamRow(name, param.default, param.required) - same rule, no description; skipped when name.toLowerCase() is authorization or content-type |
body |
set body |
sample = param.schema ? sampleSchema(param.schema, resolveRef) : {}; serialized with JSON.stringify(sample, null, 2). Mode is JSON or text per consumes (below). |
formData |
collect into formFields |
{ key: name, value: "", enabled: true } per field; a type: file parameter becomes a file part instead (see type: file fields) |
| (anything else) | ignored | no default case action |
After the loop, form data wins: if any formData fields were collected, body is unconditionally replaced with { mode: formMode, fields: formFields } - overriding any body set by an in: "body" parameter. (A spec mixing both would be unusual, but the code resolves it in favor of the form.) formMode comes from consumes, below.
Parameter values & enabled state¶
A spec's parameters list declares what an operation accepts, not what every request should send - and an enabled row reaches the wire either way: a query row through the url/params join, a header row as a header the request claims to send. declared_param_row (openapi_drafts.cpp, shared with the OpenAPI 3 parser and applied to in: "query" and in: "header" alike) decides both fields from the parameter alone:
| Parameter declares | value |
enabled |
|---|---|---|
a scalar default |
that value as text (25, false) |
true |
required: true, no default |
"" |
true - a query row joins as a bare key (?tenant), a header row is listed with an empty value; either is the cue to fill it in |
| nothing, and not required | "" |
false |
default is the only value keyword read: Swagger 2.0 has no example for a non-body parameter (the Example Object arrived with v3), and enum lists what is allowed rather than what to send. Only scalars become a value - an array or object default is serialized by collectionFormat, which this parser does not read (below), so such a parameter imports value-less. A declared "" is value-less too: an empty-value row writes as a bare key, so ?q= is not a shape the Params table can hold.
Why optional value-less parameters import disabled (issues #622, #658): the row is documentation ("this endpoint accepts verbose"), not intent ("send verbose always"). Enabled, a query row joined the stored URL as ?verbose, which some APIs read as verbose=true, and a header row claimed an X-Request-Id: with nothing in it - both a wire change nobody chose. Disabled, the row is still listed in its table one click from use.
consumes → body mode¶
consumes drives two decisions: JSON-vs-text for an in: "body" parameter, and urlencoded-vs-multipart for formData fields.
const consumes = op.consumes ?? spec.consumes ?? [];
const isJsonConsume =
consumes.length === 0 ||
consumes.some(
(c) => c === "application/json" || c.startsWith("application/json;") || c.endsWith("+json")
);
consumesis taken from the operation first, falling back to the spec-levelconsumes, else[].isJsonConsumeistruewhenconsumesis empty (default assumption: JSON) or any entry is exactlyapplication/json, starts withapplication/json;(e.g. with a charset), or ends with+json(e.g.application/hal+json).- Body mode:
isJsonConsume === true→{ mode: "json", content: JSON.stringify(sample, null, 2) }- otherwise →
{ mode: "text", content: JSON.stringify(sample, null, 2) }
Note the text branch still serializes the sampled schema to JSON text (it does not blank the body - this differs from v3's text/plain handling, which emits an empty string).
consumes → form encoding¶
Swagger 2.0 ties formData encoding to consumes, and application/x-www-form-urlencoded and multipart/form-data are distinct wire encodings that Vayu models as distinct body modes. Importing every formData operation as multipart (which is what this parser did unconditionally) sent a classic urlencoded login/token endpoint out as multipart, and the server rejected it with a 400/415 that nothing in the import explained. The rule now:
consumes (operation, else spec-level) |
Form body mode |
|---|---|
lists application/x-www-form-urlencoded and not multipart/form-data |
x-www-form-urlencoded |
lists multipart/form-data |
form-data |
| lists both | form-data - only multipart can carry a type: file field |
| absent, or names neither | form-data (the historical default is preserved) |
Entries are compared on the media type alone, so a charset/boundary parameter (application/x-www-form-urlencoded; charset=utf-8) still matches.
One case overrides the table: a form carrying a file part is always form-data, whatever consumes says. Only multipart has a file form on the wire, so a spec that declares type: file under a urlencoded-only consumes contradicts itself, and multipart is the half of the contradiction that can carry the field.
type: file fields¶
A formData parameter with type: file imports as a file part - { key, value: "", enabled: true, type: "file", src: "" } - not as a text row. Until this landed it became an ordinary empty-value text row, so an operation documenting an upload produced a request that looked healthy and sent nothing (the silent-loss class fixed for the other importers by the file-part mapping, and on the wire before that).
The part carries no path: a spec documents that a field is an upload, never which file it uploads. The user picks the file in the request editor, and the engine refuses the send by field name until they do (Form field 'avatar' is a file part with no file selected). The row is deliberately not marked unresolved - that flag warns that a path came from somewhere else and was never verified here, and there is no path to warn about; the row reads "Choose file", exactly like one a user turned into a file part by hand.
The import preview counts these as N file parts need a file, beside the skip counters, so a spec full of uploads says so before the import rather than one failed send at a time.
$ref & schema sampling¶
resolveRef resolves any JSON-pointer ref against the whole spec - Swagger model refs are #/definitions/..., but the resolver is generic. It strips the leading #/, splits on /, un-escapes ~1→/ and ~0→~, and walks the spec object segment by segment.
Body schemas are turned into stub values by Sampler::sample (openapi_drafts.cpp), which walks the schema recursively. It is bounded and recursive - materially more capable than older "one level deep" notes:
- Depth cap.
MAX_DEPTH = 6. Oncedepth > 6, the walker returns{}. Non-object / null nodes also return{}. $refresolution + cycle guard. A node with a string$ref(e.g.#/definitions/User) is resolved viaresolveRefand walked (depth +1). ASetof already-visited$refstrings is threaded down each branch; re-encountering a$refalready on the current path returns{}(breaks reference cycles). Resolution failures (throwornullresult) also yield{}.- Pinned-value precedence:
const→example→examples[0](checked after$ref, before composition andtype).constoutranksexamplebecause JSON Schema makes it the only permitted value;examplesis the plural form 3.1 introduced. Swagger 2.0 schemas only ever carryexample, so in practice this parser reads that one - the other two come along because the sampler is shared. allOf/oneOf/anyOf- first branch. If any of these is a non-empty array, the walker recurses intobranch[0]only (precedenceallOf→oneOf→anyOf). It does not mergeallOfmembers.- Type arrays. A
typewritten as an array (a JSON-Schema / OpenAPI 3.1 shape, not legal Swagger 2.0) samples its first non-"null"member; an only-"null"type samples asnull. - Type defaults:
schema.type |
Sample value |
|---|---|
string |
enum[0] if a non-empty enum is present, else "" |
integer / number |
0 |
boolean |
false |
null |
null |
array |
[ sample(items) ] if items is present, else [] (one element) |
object (or no/unknown type) |
walks each entry of properties, producing { key: sample }; {} if no properties |
Sampler::sample is shared verbatim with the v3 parser - same depth cap, cycle guard, and branch handling. Its Sampler::form_fields companion is v3-only: Swagger 2.0 form fields come from formData parameters, not from a schema, so this parser reads param.type === "file" where v3 reads format: binary.
collectionFormat for array query params¶
Not implemented. Swagger 2.0's collectionFormat (csv / ssv / tsv / pipes / multi) on array parameters is not consulted anywhere in the parser. A query parameter - array or scalar - produces exactly one KeyValueEntry:
There is no per-value expansion, no separator joining, and no multi handling. The parameter's type and items are ignored entirely, and an array default is not carried as a value for the same reason: without collectionFormat there is no separator to join it with, and joining on a guess would send what the spec did not declare. multi does not emit one entry per value - it emits the same single entry as any other query param.
Auth / security¶
Collection auth is built the same way it always was, and every request still defaults to { mode: "inherit" } so the common case - one scheme for the whole document - configures credentials once.
Primary-scheme selection in parse:
const reqName = spec.security?.[0] ? Object.keys(spec.security[0])[0] : undefined;
const defs = spec.securityDefinitions ?? {};
const primaryScheme = (reqName && defs[reqName]) || Object.values(defs)[0];
- If
spec.security[0]exists, take its first key and look it up insecurityDefinitions. - Otherwise (or if that lookup is falsy) fall back to the first entry of
securityDefinitions.
swaggerSchemeToAuth(scheme) maps the picked scheme to a concrete collection auth (always with empty secrets - the spec has no real credentials):
securityDefinitions entry |
Vayu RequestAuth |
|---|---|
type: "basic" |
{ mode: "basic", username: "", password: "" } |
type: "apiKey" |
{ mode: "apikey", key: scheme.name ?? "", value: "", in: scheme.in === "query" ? "query" : "header" } |
type: "oauth2" |
{ mode: "oauth2", config: OAuth2Config } via map_swagger_oauth2 - maps the Swagger flow (application → client-credentials, accessCode → auth-code+PKCE, password, implicit→auth-code+PKCE), fills tokenUrl/authorizationUrl/scope, seeds clientId/clientSecret as {{variables}}. A flow missing, misspelled, or of any other type is { mode: "none" }, counted under security_unmapped_type the same as any other unmapped scheme - never a fabricated client_credentials config with blank URLs, indistinguishable from a genuinely declared one. |
missing scheme / missing type / any other type |
{ mode: "none" } |
A per-operation security overrides that default the same way it does for OpenAPI 3.0 (issue #1444) - absent stays inherit, []/[{}] becomes { mode: "none" }, a requirement naming the collection's own scheme stays inherit, one naming a different securityDefinitions entry gets that entry's mapped mode, and anything Vayu cannot resolve to one mode (multiple requirements, a requirement naming multiple schemes, a requirement naming an undeclared scheme, a scheme swaggerSchemeToAuth maps to none) stays inherit and is counted under the matching security_unmapped_* kind. See OpenAPI 3.0's table for the shared rule and the full set of kinds; only the scheme source (securityDefinitions instead of components.securitySchemes) differs, and Swagger 2.0's type enum has no mutualTLS/openIdConnect to name, so this parser only ever emits security_unmapped_or, security_unmapped_and, security_unmapped_scheme and security_unmapped_type.
nonExecutableAuth: always 0 - oauth2 now maps to an executable config (as do bearer/basic/apikey).
Options & lossy behavior¶
This parser is stub-only: it materializes the shape of each request, and the only values it carries are the ones the spec states outright - a query or header parameter's default (above) and a response example. The ImportOptions argument (importEnvironments, importScripts) is ignored - the parameter is _opts and is never read (identical to v3).
Dropped / not represented:
- Scripts: all
preRequestScript/postRequestScriptare""(Swagger has no scripts;importScriptshas no effect here). - Environments: none produced (
environments: [],meta.environmentCount: 0). Swagger has no environment concept; thescheme/host/basePathtriple becomes a singlebaseUrlcollection variable. - Additional schemes: only
schemes[0]is used; other schemes are dropped. collectionFormat, parametertype/items,enumon params: not consumed.requiredand a scalardefaultare read, for query and header params alike - see Parameter values & enabled state; form params are still always empty-value stubs.- Response headers (
responses[code].headers): not imported. Response schemas and examples are, since issue #481 - see Documented responses. authorization/content-typeheader parameters: dropped (Vayu manages them).- Path parameters as params: not emitted (path params live in the URL only).
- Multi-tag grouping: only the first tag groups an operation.
- A path item, or a
parameterslist, whose shape the spec does not allow: stepped over and counted asmalformed_specso the rest of the file still imports.
meta population: format = "OpenAPI 2.0 (Swagger)", requestCount = total operations built, folderCount = number of folders (folders.count()), folderStrategy = which rule produced them, environmentCount = 0, exampleCount = example responses imported (read off the finished drafts by count_examples), nonExecutableAuth = 0 (oauth2 is now executable), unattached_file_parts = file parts imported with no file attached (unattached_file_parts, read off the finished drafts), and skipped from the shared ImportTally - malformed_spec, example_no_status, default_response, duplicate_operation_id, deprecated_operation (an operation's own deprecated: true, which Vayu's request model has no field for), and security_unmapped_or / security_unmapped_and / security_unmapped_scheme / security_unmapped_type (issue #1444) are the only kinds this parser can emit (Swagger 2.0's Path Item Object has no trace, so there is no unsupported_method case here). The three kinds issue #719 added, and servers_dropped / webhook_operations (issue #1444), are v3-only for the same kind of reason: Swagger 2.0 has no in: "cookie" parameter, no Server Object to template, leave relative or declare more than one of - host is a single host - no webhooks top-level object, and every declared body maps to one, so cookie_param, unresolved_base_url, unmapped_body, servers_dropped and webhook_operations have no case here either. Nothing to report still yields [].
Differences from OpenAPI 3.0¶
See OpenAPI v3 for the v3 reference. Key contrasts:
| Aspect | v2 (Swagger) | v3 (OpenAPI 3.0) |
|---|---|---|
| Detection | swagger === "2.0", or the number 2 from unquoted YAML |
openapi is a string starting with "3." |
| Base URL | schemes[0] + host + basePath |
servers[0].url |
| Request body | in: "body" / in: "formData" parameters |
dedicated op.requestBody with content map |
| Body content-type decision | consumes (op → spec → JSON default) |
media-type keys of requestBody.content |
| Text/non-JSON body | sampled schema serialized as JSON text | text/plain → empty string |
| Form bodies | in: "formData" params → urlencoded or multipart per consumes (overrides body param) |
multipart/form-data / x-www-form-urlencoded from content, field names resolved through the sampler |
| File parts | type: "file" formData parameter |
format: "binary" (or an array of it) property under multipart/form-data |
| Unsupported methods | none - Swagger 2.0 defines no trace |
trace counted as unsupported_method |
$ref namespace |
#/definitions/... |
#/components/schemas/... (resolver is generic in both) |
| Auth schemes | securityDefinitions (basic, apiKey, oauth2) |
components.securitySchemes (http/bearer/basic, apiKey, oauth2) |
| Auth helper | swaggerSchemeToAuth |
schemeToAuth |
| Collection build | root inline in parse; folders from the shared OperationFolders |
root inline in parse; folders from the shared OperationFolders |
Shared between both: the folder routing (OperationFolders - first tag, else path segment), {{baseUrl}}-prefixed URLs, normalize_template_vars path conversion, Sampler::sample, request auth: inherit, ImportOptions ignored, and the openapi_drafts.cpp helpers (walk::resolve_single_hop, ImportTally) - so a $ref'd path item, a malformed parameters list, and meta.skipped behave identically in both.
Shared helpers used¶
| Helper | Source | Use in this parser |
|---|---|---|
normalize_template_vars |
path_template.cpp |
convert Swagger {param} path templates → Vayu {{param}} in request URLs |
Sampler::sample |
openapi_drafts.cpp |
generate a sample JSON body from an in: "body" parameter schema (bounded, ref-resolving) |
walk::resolve_ref, walk::resolve_single_hop, ImportTally |
openapi_drafts.cpp |
resolve an in-document $ref and a $ref'd path item; guard parameters and tally what was dropped |
bundleExternalRefs |
ref-bundler.ts |
resolve references to other files before parse, and count what it could not reach |
response_example, example_body_text, deref |
openapi_drafts.cpp |
map one responses entry to an example draft - the half shared with the v3 parser |
declared_param_row, param_value_text |
openapi_drafts.cpp |
one in: "query" or in: "header" parameter as a table row - the value/enabled rule both OpenAPI parsers apply |
count_examples |
import_document.cpp |
total the examples across the finished drafts, for meta.exampleCount |
Beyond count_examples, this parser does not use the Postman/Insomnia helpers in import_document.cpp (as_string, to_var_record, map_key_values, map_postman_auth, raw_body, join_exec); it builds drafts directly. See the index for the full shared-helper reference.