# Vert.x OpenAPI

Vert.x OpenAPI extends Vert.x Web to support OpenAPI 3, bringing to you a simple interface to build a Vert.x Web Router conforming your API contract.

Vert.x OpenAPI can:

- Parse and validate the your OpenAPI 3 contract

- Generate a router according to your spec, with correct path & methods

- Provide request parsing and validation based on your contract using Vert.x Web Validation

- Mount required security handlers

- Path conversion between OpenAPI style and Vert.x style

- Route requests to event bus using Vert.x Web API Service

## 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`):

    <dependency>
     <groupId>io.vertx</groupId>
     <artifactId>vertx-web-openapi</artifactId>
     <version>4.0.3</version>
    </dependency>

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

    dependencies {
     compile 'io.vertx:vertx-web-openapi:4.0.3'
    }

## `RouterBuilder`

`RouterBuilder` is the main element of this module: It provides the interface to mount request handlers and generates the final `Router`

To start using Vert.x Web OpenAPI, you must instantiate `RouterBuilder` with your contract using `RouterBuilder.create`

For example to load a spec from the local filesystem:

    RouterBuilder.create(vertx, "src/main/resources/petstore.yaml").onComplete(ar -> {
      if (ar.succeeded()) {
        // Spec loaded with success
        RouterBuilder routerBuilder = ar.result();
      } else {
        // Something went wrong during router builder initialization
        Throwable exception = ar.cause();
      }
    });

You can construct a router builder from a remote spec:

    RouterBuilder.create(
      vertx,
      "https://raw.githubusercontent" +
        ".com/OAI/OpenAPI-Specification/master/examples/v3.0/petstore.yaml"
    ).onComplete(ar -> {
      if (ar.succeeded()) {
        // Spec loaded with success
        RouterBuilder routerBuilder = ar.result();
      } else {
        // Something went wrong during router builder initialization
        Throwable exception = ar.cause();
      }
    });

You can access a private remote spec configuring `OpenAPILoaderOptions`:

    OpenAPILoaderOptions loaderOptions = new OpenAPILoaderOptions()
      .putAuthHeader("Authorization", "Bearer xx.yy.zz");
    RouterBuilder.create(
      vertx,
      "https://raw.githubusercontent" +
        ".com/OAI/OpenAPI-Specification/master/examples/v3.0/petstore.yaml",
      loaderOptions
    ).onComplete(ar -> {
      if (ar.succeeded()) {
        // Spec loaded with success
        RouterBuilder routerBuilder = ar.result();
      } else {
        // Something went wrong during router builder initialization
        Throwable exception = ar.cause();
      }
    });

You can modify different behaviours of the router builder with `RouterBuilderOptions`:

    routerBuilder.setOptions(new RouterBuilderOptions());

## Access to operations

To access to an Operation defined in contract, use `operation`. This method returns an `Operation` instance that you can use to both access the model and assign handlers

To mount an handler to an operation use `handler`, to mount a failure handler use `failureHandler`

You can add multiple handlers to same operation, without overwrite the existing ones.

For example:

    routerBuilder
      .operation("awesomeOperation")
      .handler(routingContext -> {
        RequestParameters params =
          routingContext.get(ValidationHandler.REQUEST_CONTEXT_KEY);
        RequestParameter body = params.body();
        JsonObject jsonBody = body.getJsonObject();
        // Do something with body
      }).failureHandler(routingContext -> {
      // Handle failure
    });

Important | You can’t access to contract operations without `operationId`. The operations without `operationId` are ignored by the `RouterBuilder`

Vert.x OpenAPI mounts the correct `ValidationHandler` for you, so you can access to request parameters and request body. Refer to Vert.x Web Validation documentation to learn how to get request parameters & request body and how to manage validation failures

## Map `AuthenticationHandler` to OpenAPI security schemes

You can map an `AuthenticationHandler` to a security schema defined in the contract.

For example, given your contract has a security schema named `security_scheme_name`:

    routerBuilder.securityHandler("security_scheme_name", authenticationHandler);

You can mount `AuthenticationHandler` included in Vert.x Web, for example:

    routerBuilder.securityHandler("jwt_auth",
      JWTAuthHandler.create(jwtAuthProvider));

When you generate the `Router` the router builder will solve the security schemes required for an operation. It fails if there is a missing `AuthenticationHandler` required by a configured operation.

For debugging/testing purpose you can disable this check with `setRequireSecurityHandlers`

## Not Implemented Error

Router builder automatically mounts a default handler for operations without a specified handler. This default handler fails the routing context with 405 `Method Not Allowed`/501 `Not Implemented` error. You can enable/disable it with `setMountNotImplementedHandler` and you can customize this error handling with `errorHandler`

## Response Content Type Handler

Router builder automatically mounts a `ResponseContentTypeHandler` handler when contract requires it. You can disable this feature with `setMountResponseContentTypeHandler`

## Operation model

If you need to access to your operation model while handling the request, you can configure the router builder to push it inside the `RoutingContext` with `setOperationModelKey`:

    options.setOperationModelKey("operationModel");
    routerBuilder.setOptions(options);

    // Add an handler that uses the operation model
    routerBuilder
      .operation("listPets")
      .handler(
        routingContext -> {
          JsonObject operation = routingContext.get("operationModel");

          routingContext
            .response()
            .setStatusCode(200)
            .setStatusMessage("OK")
            // Write the response with operation id "listPets"
            .end(operation.getString("operationId"));
        });

## Body Handler

Router builder automatically mounts a `BodyHandler` to manage request bodies. You can configure the instance of `BodyHandler` (e.g. to change upload directory) with `bodyHandler`.

## `multipart/form-data` validation

The validation handler separates file uploads and form attributes as explained:

- If the parameter doesn’t have an encoding associated field:

  - If the parameter has `type: string` and `format: base64` or `format: binary` is a file upload with content-type `application/octet-stream`

  - Otherwise is a form attribute

- If the parameter has the encoding associated field is a file upload

The form attributes are parsed, converted in json and validated, while for file uploads the validation handler just checks the existence and the content type.

## Custom global handlers

If you need to mount handlers that must be executed for each operation in your router before the operation specific handlers, you can use `rootHandler`

## Router builder handlers mount order

Handlers are loaded by the router builder in this order:

1. Body handler

2. Custom global handlers

3. Configured `AuthenticationHandler`s

4. Generated `ValidationHandler`

5. User handlers or "Not implemented" handler (if enabled)

## Generate the router

When you are ready, generate the router and use it:

    Router router = routerBuilder.createRouter();

    HttpServer server =
      vertx.createHttpServer(new HttpServerOptions().setPort(8080).setHost(
        "localhost"));
    server.requestHandler(router).listen();

This method can fail with a `RouterBuilderException`.

Tip | If you need to mount all the router generated by router builder under the same parent path, you can use `mountSubRouter`:
    Router global = Router.router(vertx);

    Router generated = routerBuilder.createRouter();
    global.mountSubRouter("/v1", generated);