# convert

Source: https://apigenie.pl/techdocs/reference/cli-convert

Convert a Swagger 2.0 document to OpenAPI 3.2, or a WSDL 1.1 document to OpenAPI 3.1.

> **(experimental, outside the 1.0 compatibility promise)** `convert` has two sources, Swagger 2.0
> and WSDL 1.1, and its options and output can still change. It says so in its `--help` and on the
> first line it prints, on the standard error. See the
> [experimental commands](/techdocs/reference/cli-overview#experimental-commands).

## Usage

```sh
apigenie convert --to openapi32 [...OPTIONS] SWAGGER_FILE
apigenie convert --to openapi31 [...OPTIONS] WSDL_FILE
```

## Description

The command reads a Swagger 2.0 document or a WSDL 1.1 document and writes the equivalent OpenAPI
document, to the standard output or to the file given with `--output`. Each source has one target:
a Swagger document becomes OpenAPI 3.2 (`--to openapi32`), a WSDL document OpenAPI 3.1
(`--to openapi31`). The source is the one `--from` names; without `--from`, an input that starts
with `<` (an XML document) is WSDL, and any other input Swagger.

## Swagger 2.0 input

The command reads a Swagger 2.0 document (`swagger: "2.0"`, JSON or YAML) and writes the
equivalent OpenAPI 3.2 document. OpenAPI 3.x documents are not accepted as input: they are read as
Swagger documents and rejected (`Missing required property 'swagger'`).

The document is checked first. When it isn't a valid Swagger 2.0 document, the command lists the
problems with their lines and columns and writes nothing.

The conversion changes the structure of the document, not only its references:

- `body` and `formData` parameters become request bodies, with a media type per entry of
  `consumes` (file uploads as `multipart/form-data`). Global body parameters become
  `components.requestBodies`.
- Other parameters get a `schema` built from their keywords, and `collectionFormat` becomes `style`
  and `explode`.
- Response schemas, headers and examples move into `content`, with a media type per entry of
  `produces`.
- `host`, `basePath` and `schemes` become `servers`: one URL per scheme, `https` when no scheme is
  given, and a relative URL from `basePath` alone when there is no `host`.
- `definitions`, `parameters`, `responses` and `securityDefinitions` move into `components`, and
  references follow them (`#/definitions/Pet` becomes `#/components/schemas/Pet`).
- `basic` security schemes become `http` schemes with `scheme: basic`; the `flow`, URLs and scopes
  of `oauth2` schemes move into `flows` (`accessCode` becomes `authorizationCode`, `application`
  becomes `clientCredentials`).
- Schemas switch dialect: `type: file` becomes a binary string, discriminators become objects,
  boolean `exclusiveMinimum` and `exclusiveMaximum` become numeric bounds, and `x-nullable: true`
  adds `null` to the type.
- Specification extensions (`x-...`) and everything else without a counterpart to convert are
  copied as they are.

Some parts of a Swagger 2.0 document have no exact counterpart in OpenAPI 3.2 and are approximated
or dropped: a `collectionFormat` of `tsv` (or one other than `csv` in a path or a header, or other
than `multi` in a form field), `allowEmptyValue` of a form field, an operation's own `schemes`,
form parameters next to a body parameter, and `x-nullable` without a `type`. A reference to
another document is copied unchanged, so that document has to be converted too; bundle the
document first with [`apigenie bundle`](/techdocs/reference/cli-bundle) to convert it as a whole.
The `convert` command doesn't report these approximations yet;
[`apigenie-server`](/techdocs/reference/cli-server#swagger-20-documents) and
[`apigenie test`](/techdocs/reference/cli-test) do (see below).

The output keeps the order of the input's keys. Comments of a YAML input are not kept.

### Swagger 2.0 in apigenie-server and apigenie test

`apigenie-server`, its validating proxy (`--proxy-to`), and `apigenie test --spec` take Swagger 2.0
documents directly: they convert them to OpenAPI 3.2 in memory, as `convert --to openapi32` does,
each time the document is read, and write no file. Locations stay those of the Swagger file: the
lines of warnings, the lines and JSON pointers of `x-apigenie-mock` problems, and the `pointer` of
each operation in the contract reports. What the conversion can only approximate is told once at
startup as `info:` lines with the Swagger file's line and JSON pointer (`{"event":"info",...}` with
`apigenie test --formatter json`). See
[Swagger 2.0 documents](/techdocs/reference/cli-server#swagger-20-documents).

The other commands that need an OpenAPI document, such as the
[generators](/techdocs/reference/cli-generate), read only OpenAPI 3.x: convert Swagger 2.0
documents first.

## WSDL 1.1 input

The command reads a WSDL 1.1 document whose SOAP 1.1 or SOAP 1.2 bindings are document/literal
over HTTP, with the XML Schemas of its types, and writes an OpenAPI 3.1 document. The result lints
without errors with the `recommended` ruleset (see [Linting](#linting-the-result)). It serves in
[`apigenie-server`](/techdocs/reference/cli-server), which validates SOAP requests and answers
SOAP envelopes. [`diff`](/techdocs/reference/cli-diff) and the
[generators](/techdocs/reference/cli-generate) read it like any other OpenAPI document.

The WSDL documents it imports (`wsdl:import`) and the schemas (`xs:import` and `xs:include` with a
`schemaLocation`) are read from files, relative to the document that names them. Remote documents
(`http:` and `https:`) are not fetched: download them and point the `location` or
`schemaLocation` at the copies. A schema that isn't fetched is reported, and the elements, types
and attributes of its namespace then accept any value.

### Operations

Each operation of a SOAP binding becomes a `POST` operation:

- **Path:** the path of the port's `soap:address`, followed by the operation's name. The
  operations of a port share one address in SOAP, but an OpenAPI path has one `POST`, so each
  operation gets a path of its own. For `https://api.example.com/soap/greeter`, the operation
  `Greet` is `POST /soap/greeter/Greet`. The address's origin is the server. A binding that no
  port uses gets `/{binding}/{operation}` and no server.
- **Extensions:** `x-soap-address` is the port's address, and `x-soap-action` the operation's
  SOAPAction.
- **Tag and operationId:** the tag is the port type. The operationId is the operation's name,
  prefixed with the port type (or the port) when several operations have that name.
- **Description:** the operation's `wsdl:documentation`, or a sentence naming the operation when
  it has none.
- **SOAP 1.1 and 1.2:** a binding of each version for one port type at one address makes one
  operation, with a `text/xml` body (the SOAP 1.1 envelope) and an `application/soap+xml` body
  (the SOAP 1.2 envelope). `apigenie-server` answers in the media type of the request.

### Bodies and responses

Request and response bodies are SOAP envelopes. Their schemas carry the
[`xml` objects](/techdocs/reference/cli-server#xml-bodies) that map the envelope's XML to the
schema and back:

- **Envelope:** the root is `soap:Envelope`. Its `Body` holds an element per body part of the
  message, and its `Header` the `soap:header` parts. The Header also accepts header blocks that
  the WSDL doesn't declare, such as WS-Addressing's.
- **Responses:** the output message is the `200` response. A one-way operation answers `202`
  without content.
- **Faults:** the `500` response is a SOAP Fault, with the faults of the operation in its
  `detail` (SOAP 1.1) or `Detail` (SOAP 1.2).

The global elements and types the messages use become schemas in `components`:

- **Elements and types:** an element and a type of the same name are `X` and `XType`.
- **Content models:** `xs:sequence` and `xs:all` become objects with `additionalProperties:
  false`. `minOccurs` gives `required`, and a repeated element becomes an array whose items repeat
  in the parent element. A required `xs:choice` of required elements becomes `oneOf` of its
  alternatives.
- **Attributes and text:** an attribute is a property with `xml: {attribute: true}`. The text of
  an element with simple content is a `value` property with `xml: {x-nodeType: text}`.
- **Facets and values:** `nillable` adds `null` to the type, `default` becomes `default`, and
  `fixed` becomes `const`. Enumerations, lengths, bounds and patterns become their JSON Schema
  keywords.
- **Built-in types:** each gets a type and a format; an `xs:int` is `{type: integer, format:
  int32}`, an `xs:dateTime` is `{type: string, format: date-time}`.
- **Namespaces:** every `xml` object with a namespace has a prefix, the one the WSDL declares when
  it is free. Elements are qualified as `elementFormDefault` (or `form`) says.
- **Descriptions:** `xs:documentation` becomes the description.

### Limits

What has no exact counterpart is approximated, and told on the standard error as a warning with
its file, line and column. Warnings with the same text are told once, followed by "(and N more
like this)".

- `xs:any`, `xs:anyAttribute` and `xs:anyType` accept any content: the object gets
  `additionalProperties: true` and `x-apigenie-lint-disable: [schema-no-additional-properties]`.
- Substitution groups aren't followed: only the head element is accepted where it is used.
  Abstract elements and types are accepted as they are declared.
- A type extending another one copies its content. An element of a derived type (`xsi:type`)
  isn't accepted in place of its base.
- Mixed content: the text between child elements isn't described.
- A list type becomes a string of space-separated values.
- A repeated `xs:sequence` or `xs:choice` becomes an array per element; the order across elements
  is lost.
- `xs:decimal` becomes a `double`, and integer types without bounds (`xs:integer`,
  `xs:nonNegativeInteger`...) an `int64`.
- `totalDigits` is dropped, and so is `fractionDigits` other than 0. Patterns using `\i`, `\c`,
  `\p{Is...}` or character class subtraction are dropped.
- `xs:redefine` and `xs:override` are read as `xs:include`.
- A message part with a `type` instead of an `element` becomes an element in no namespace.
- HTTP and MIME bindings are left out. So are operations in rpc style or with encoded use, next to
  document/literal ones.
- WS-Policy, WS-Addressing, WS-Security and other WS-* extensions aren't read, and neither are
  MIME attachments (MTOM, SwA).
- WSDL has no version: `info.version` is `1.0.0`.

These documents are refused, with exit status 2:

- documents that aren't well-formed XML, and WSDL 2.0 documents;
- documents with a missing local import, or with a reference to something that isn't defined;
- documents without a document/literal SOAP binding over HTTP: rpc/encoded and rpc/literal
  services, and SOAP over other transports. The message names the binding and its style and use.

The importer converts WSDL to OpenAPI only: it doesn't generate SOAP clients or servers, and it
doesn't write WSDL.

### Linting the result

The converted document has no errors with the `recommended` ruleset. Some warnings stay, because
they concern what WSDL doesn't describe or names WSDL sets:

- security and rate limits: `operation-security-defined` and `operation-rate-limiting-defined`;
- the length of strings: `schema-constraints`;
- a binding without an address: `servers-defined`;
- large services: `paths-max-count`;
- the address's case: `paths-well-formed`;
- date element names: `schema-datetime-suffix`.

Two more come from XML Schema constructs:

- `schema-discriminator-defined`: the alternatives of an `xs:choice` are told apart by their
  element, not by a property;
- `schema-no-optional-nullable-properties`: an optional nillable element can be absent or
  `xsi:nil`, which differ in XML.

## Options

### --from

The source specification: `swagger2` (Swagger 2.0) or `wsdl` (WSDL 1.1). Without it, an XML input
is WSDL and any other input Swagger 2.0.

### --to

Required. The target specification and version: `openapi32` (OpenAPI 3.2) for Swagger 2.0, and
`openapi31` (OpenAPI 3.1) for WSDL. Another pair exits with status 2.

### --format

Serialization of the output: `json` or `yaml`. Defaults to the format of the input, and to YAML for
WSDL.

### -o, --output

Path of the converted document. Without it, the document is written to the standard output.

## Exit status

| Status | Meaning |
| ------ | ------- |
| 0 | The document was converted |
| 2 | Invalid arguments (a missing `--to`, an unknown target or format, a target the source doesn't convert to), the input file can't be read, it isn't a valid Swagger 2.0 document, or the WSDL document can't be converted (the problems are listed on the standard error) |

## Examples

Convert a YAML document and print the result:

```sh
apigenie convert --to openapi32 petstore.yaml
```

Convert to a JSON file:

```sh
apigenie convert --to openapi32 --format json -o petstore.openapi.json petstore.yaml
```

Convert a WSDL document, lint the result and serve it:

```sh
apigenie convert --to openapi31 -o greeter.yaml greeter.wsdl
apigenie lint greeter.yaml
apigenie-server greeter.yaml
```

For this Swagger 2.0 input:

```yaml
swagger: "2.0"
info: {title: Pets, version: "1.0"}
host: api.example.com
basePath: /v1
schemes: [https, http]
consumes: [application/json]
produces: [application/json]
securityDefinitions:
  basic: {type: basic}
paths:
  /pets:
    post:
      operationId: addPet
      parameters:
        - {name: body, in: body, required: true, schema: {$ref: '#/definitions/Pet'}}
      responses:
        "201": {description: Created, schema: {$ref: '#/definitions/Pet'}}
definitions:
  Pet:
    type: object
    properties:
      name: {type: string, x-nullable: true}
      photo: {type: file}
```

the command writes:

```yaml
openapi: 3.2.0
info:
  title: Pets
  version: "1.0"
servers:
- url: https://api.example.com/v1
- url: http://api.example.com/v1
paths:
  /pets:
    post:
      operationId: addPet
      responses:
        "201":
          description: Created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Pet"
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/Pet"
        required: true
components:
  schemas:
    Pet:
      type: object
      properties:
        name:
          type:
          - string
          - "null"
        photo:
          type: string
          format: binary
  securitySchemes:
    basic:
      type: http
      scheme: basic
```

For this WSDL input:

```xml
<definitions name="Greeter" targetNamespace="urn:example:greeter"
    xmlns="http://schemas.xmlsoap.org/wsdl/"
    xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:g="urn:example:greeter">
  <types>
    <xs:schema targetNamespace="urn:example:greeter" elementFormDefault="qualified">
      <xs:element name="Greet">
        <xs:complexType>
          <xs:sequence>
            <xs:element name="name" type="xs:string"/>
            <xs:element name="times" type="xs:int" minOccurs="0"/>
          </xs:sequence>
        </xs:complexType>
      </xs:element>
      <xs:element name="GreetResponse">
        <xs:complexType>
          <xs:sequence>
            <xs:element name="greeting" type="xs:string" maxOccurs="unbounded"/>
          </xs:sequence>
        </xs:complexType>
      </xs:element>
    </xs:schema>
  </types>
  <message name="GreetIn"><part name="parameters" element="g:Greet"/></message>
  <message name="GreetOut"><part name="parameters" element="g:GreetResponse"/></message>
  <portType name="Greeter">
    <operation name="Greet">
      <documentation>Greets someone.</documentation>
      <input message="g:GreetIn"/>
      <output message="g:GreetOut"/>
    </operation>
  </portType>
  <binding name="GreeterSoap" type="g:Greeter">
    <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
    <operation name="Greet">
      <soap:operation soapAction="urn:example:greeter/Greet"/>
      <input><soap:body use="literal"/></input>
      <output><soap:body use="literal"/></output>
    </operation>
  </binding>
  <service name="GreeterService">
    <port name="GreeterPort" binding="g:GreeterSoap">
      <soap:address location="https://api.example.com/soap/greeter"/>
    </port>
  </service>
</definitions>
```

the command writes (the 500 response and the shared `Soap11Header` and `Soap11Fault` components
left out):

```yaml
openapi: 3.1.0
info:
  title: GreeterService
  summary: The SOAP service GreeterService, converted from WSDL
  description: The operations of the SOAP service GreeterService. Each operation takes and returns a SOAP envelope; its path is the address of the service followed by the name of the operation.
  version: 1.0.0
  x-wsdl-target-namespace: urn:example:greeter
servers:
- url: https://api.example.com
tags:
- name: Greeter
  description: The operations of the port type Greeter.
paths:
  /soap/greeter/Greet:
    post:
      tags:
      - Greeter
      description: Greets someone.
      operationId: Greet
      requestBody:
        required: true
        content:
          text/xml:
            schema:
              type: object
              properties:
                Header:
                  $ref: "#/components/schemas/Soap11Header"
                Body:
                  type: object
                  xml:
                    namespace: http://schemas.xmlsoap.org/soap/envelope/
                    prefix: soap
                  properties:
                    Greet:
                      $ref: "#/components/schemas/Greet"
                      xml:
                        name: Greet
                        namespace: urn:example:greeter
                        prefix: g
                  required:
                  - Greet
                  additionalProperties: false
              required:
              - Body
              additionalProperties: false
              xml:
                name: Envelope
                namespace: http://schemas.xmlsoap.org/soap/envelope/
                prefix: soap
      responses:
        "200":
          description: The response of Greet.
          content:
            text/xml:
              schema:
                type: object
                properties:
                  Header:
                    $ref: "#/components/schemas/Soap11Header"
                  Body:
                    type: object
                    xml:
                      namespace: http://schemas.xmlsoap.org/soap/envelope/
                      prefix: soap
                    properties:
                      GreetResponse:
                        $ref: "#/components/schemas/GreetResponse"
                        xml:
                          name: GreetResponse
                          namespace: urn:example:greeter
                          prefix: g
                    required:
                    - GreetResponse
                    additionalProperties: false
                required:
                - Body
                additionalProperties: false
                xml:
                  name: Envelope
                  namespace: http://schemas.xmlsoap.org/soap/envelope/
                  prefix: soap
      x-soap-action: urn:example:greeter/Greet
      x-soap-address: https://api.example.com/soap/greeter
components:
  schemas:
    Greet:
      type: object
      properties:
        name:
          type: string
          xml:
            namespace: urn:example:greeter
            prefix: g
        times:
          type: integer
          format: int32
          xml:
            namespace: urn:example:greeter
            prefix: g
      required:
      - name
      additionalProperties: false
      xml:
        name: Greet
        namespace: urn:example:greeter
        prefix: g
    GreetResponse:
      type: object
      properties:
        greeting:
          type: array
          items:
            type: string
            xml:
              namespace: urn:example:greeter
              prefix: g
          minItems: 1
      required:
      - greeting
      additionalProperties: false
      xml:
        name: GreetResponse
        namespace: urn:example:greeter
        prefix: g
```

`apigenie-server greeter.yaml` then answers this request with a `GreetResponse` envelope, and
a request without `name` with a 422 problem (`body/Body/Greet: required property 'name' is
missing`):

```sh
curl -X POST http://127.0.0.1:4010/soap/greeter/Greet -H 'Content-Type: text/xml' \
  --data '<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body>
    <g:Greet xmlns:g="urn:example:greeter"><g:name>Ada</g:name></g:Greet>
  </soap:Body></soap:Envelope>'
```
