= Vert.x OpenAPI
:toc: left

Vert.x OpenAPI extends Vert.x to support https://www.openapis.org/[OpenAPI 3] in version 3.0 and 3.1.

Vert.x OpenAPI can:

* parse and validate your OpenAPI contract.
* parse and validate incoming requests according to your OpenAPI contract.
* parse and validate outgoing responses according to your OpenAPI contract.

== Using Vert.x OpenAPI

To use Vert.x OpenAPI, add the following dependency to the _dependencies_ section of your build descriptor:

* Maven (in your `pom.xml`):

[source,xml,subs="+attributes"]
----
<dependency>
  <groupId>io.vertx</groupId>
  <artifactId>vertx-openapi</artifactId>
  <version>5.0.0.CR1</version>
</dependency>
----

* Gradle (in your `build.gradle` file):

[source,groovy,subs="+attributes"]
----
dependencies {
  compile 'io.vertx:vertx-openapi:5.0.0.CR1'
}
----

== `OpenAPIContract`

When using Vert.x OpenAPI you always start with creating an instance of `link:../../apidocs/io/vertx/openapi/contract/OpenAPIContract.html[OpenAPIContract]` from your contract.

[source,java]
----
String pathToContract = ".../.../myContract.json"; // json or yaml
Future<OpenAPIContract> contract = OpenAPIContract.from(vertx, pathToContract);
----

NOTE: Due to security reasons this library is not downloading external references from your contract.
In case your contract requires external resources, they must be downloaded upfront and also provided to the
`link:../../apidocs/io/vertx/openapi/contract/OpenAPIContract.html[OpenAPIContract]`.

The example below shows a snippet from an example OpenAPI contract that includes a reference to an external resource and how to create an instance of `link:../../apidocs/io/vertx/openapi/contract/OpenAPIContract.html[OpenAPIContract]`.

[source,yaml]
----
paths:
  /pets:
    get:
      operationId: listPets
      parameters:
        - name: query
          schema:
            $ref: 'https://example.com/pet-components#/components/schemas/Query'
----

[source,java]
----
String pathToContract = ".../.../myContract.json"; // json or yaml
String pathToComponents = ".../.../myComponents.json"; // json or yaml
Map<String, String> additionalContractFiles = new HashMap<>();
additionalContractFiles.put("https://example.com/pet-components",
  pathToComponents);

Future<OpenAPIContract> contract =
  OpenAPIContract.from(vertx, pathToContract, additionalContractFiles);
----

NOTE: During the instantiation of `link:../../apidocs/io/vertx/openapi/contract/OpenAPIContract.html[OpenAPIContract]` the contract gets validated.
In case your contract does not match the OpenAPI specification or uses features which are not yet supported an error is thrown.

=== Path, Operation, Parameter

The `link:../../apidocs/io/vertx/openapi/contract/OpenAPIContract.html[OpenAPIContract]` interface offers methods to navigate to the `link:../../apidocs/io/vertx/openapi/contract/Path.html[Path]`,
`link:../../apidocs/io/vertx/openapi/contract/Operation.html[Operation]` and `link:../../apidocs/io/vertx/openapi/contract/Parameter.html[Parameter]` objects of the OpenAPI contract.

[source,java]
----
OpenAPIContract contract = getContract();

for (Path path : contract.getPaths()) {
  for (Parameter pathParameter : path.getParameters()) {
    // example methods of a OpenAPI parameter object
    pathParameter.isRequired();
    pathParameter.getSchema();
  }
  for (Operation operation : path.getOperations()) {
    // example methods of a OpenAPI operation object
    operation.getOperationId();
    operation.getRequestBody();
    operation.getParameters();
  }
}
----

== Validation of Requests

The `link:../../apidocs/io/vertx/openapi/validation/RequestValidator.html[RequestValidator]` offers multiple _validate_ methods to validate incoming requests.

[source,java]
----
OpenAPIContract contract = getContract();
RequestValidator validator = RequestValidator.create(vertx, contract);

vertx.createHttpServer().requestHandler(httpServerRequest -> {
  // Operation id must be determined for every request which is inefficient
  validator.validate(httpServerRequest).onSuccess(validatedRequest -> {
    validatedRequest.getBody(); // returns the body
    validatedRequest.getHeaders(); // returns the header
    // ..
    // ..
  });

  // Operation id will be passed to save effort for determining
  validator.validate(httpServerRequest, "yourOperationId")
    .onSuccess(validatedRequest -> {
      // do something
    });
}).listen(0);
----

The `link:../../apidocs/io/vertx/openapi/validation/RequestValidator.html[RequestValidator]` also offers a signature of the _validate_ method that consumes a `link:../../apidocs/io/vertx/openapi/validation/ValidatableRequest.html[ValidatableRequest]`.

[source,java]
----
OpenAPIContract contract = getContract();
RequestValidator validator = RequestValidator.create(vertx, contract);

ValidatableRequest request = getValidatableRequest();
validator.validate(request, "yourOperationId").onSuccess(validatedRequest -> {
  validatedRequest.getBody(); // returns the body
  validatedRequest.getHeaders(); // returns the header
  // ..
  // ..
});
----

NOTE: The parameters in a `link:../../apidocs/io/vertx/openapi/validation/ValidatableRequest.html[ValidatableRequest]` must be stored in a specific format depending on the style, location and if they are exploded or not, otherwise the `link:../../apidocs/io/vertx/openapi/validation/RequestValidator.html[RequestValidator]` can't validate the request.
The required format *MUST* exactly look like as described in the JavaDoc of `link:../../apidocs/io/vertx/openapi/validation/RequestValidator.html[RequestValidator]`.

== Validation of Responses

The `link:../../apidocs/io/vertx/openapi/validation/ResponseValidator.html[ResponseValidator]` offers a _validate_ method to validate responses. `link:../../apidocs/io/vertx/openapi/validation/ValidatableResponse.html[ValidatableResponse]` offers multiple _create_ methods to build validatable responses easily.

In case that the validation of a response has passed, the returned `link:../../apidocs/io/vertx/openapi/validation/ValidatedResponse.html[ValidatedResponse]` can directly be sent back to the client.

[source,java]
----
OpenAPIContract contract = getContract();
ResponseValidator validator = ResponseValidator.create(vertx, contract);

JsonObject cat = new JsonObject().put("name", "foo");
ValidatableResponse response =
  ValidatableResponse.create(200, cat.toBuffer(), APPLICATION_JSON.toString());

vertx.createHttpServer().requestHandler(httpServerRequest -> {
  validator.validate(response, "yourOperationId")
    .onSuccess(validatedResponse -> {
      validatedResponse.getBody(); // returns the body
      validatedResponse.getHeaders(); // returns the header
      // ..
      // ..
      // send back the validated response
      validatedResponse.send(httpServerRequest.response());
    });
});
----

NOTE: The parameters in a `link:../../apidocs/io/vertx/openapi/validation/ValidatableResponse.html[ValidatableResponse]` must be stored in a specific format depending on the style, location and if they are exploded or not, otherwise the `link:../../apidocs/io/vertx/openapi/validation/ResponseValidator.html[ResponseValidator]` can't validate the response.
The required format *MUST* exactly look like as described in the JavaDoc of `link:../../apidocs/io/vertx/openapi/validation/ResponseValidator.html[ResponseValidator]`.

== Handle Validation Exceptions

A `link:../../apidocs/io/vertx/openapi/validation/ValidatorException.html[ValidatorException]` is thrown, if the validation of a request or response fails.
The validation can fail for formal reasons, such as the wrong format for a parameter or the absence of a required parameter.
However, validation can of course also fail because the content does not match the defined schema.
In this case a `link:../../apidocs/io/vertx/openapi/validation/SchemaValidationException.html[SchemaValidationException]` is thrown.
It is a subclass of _ValidatorException_ and provides access to the related `link:../../apidocs/io/vertx/json/schema/OutputUnit.html[OutputUnit]` to allow further analysis of the error.