# 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.5.15</version>
    </dependency>

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

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

## `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")
      .onSuccess(routerBuilder -> {
        // Spec loaded with success
      })
      .onFailure(err -> {
        // Something went wrong during router builder initialization
      });

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")
      .onSuccess(routerBuilder -> {
        // Spec loaded with success
      })
      .onFailure(err -> {
        // Something went wrong during router builder initialization
      });

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)
      .onSuccess(routerBuilder -> {
        // Spec loaded with success
      })
      .onFailure(err -> {
        // Something went wrong during router builder initialization
      });

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

## Configuring `AuthenticationHandler`s defined in the OpenAPI document

Security is a important aspect of any API. OpenAPI defines how security is expected to be enforced in the api document.

All security scheme information resided under the `/components/securitySchemes` component. The information in this object is different and specific for each type of authentication. To avoid double configuration, this module allows you to provide factories for authentication handlers that will receive the source configuration from the source document.

For example, given an document that defines `Basic Authentication` as follows:

    openapi: 3.0.0
    ...
    components:
     securitySchemes:
       basicAuth:     # <-- arbitrary name for the security scheme
         type: http
         scheme: basic

This can be configured with a factory as:

    routerBuilder
      .securityHandler("basicAuth")
      .bindBlocking(config -> BasicAuthHandler.create(authProvider));

While this example is quite simple to configure, creating an authentication handler that requires the configuration such as the API Key handler can extract the config:

     openapi: 3.0.0
     ...
     # 1) Define the key name and location
     components:
       securitySchemes:
         ApiKeyAuth:        # arbitrary name for the security scheme
           type: apiKey
           in: header       # can be "header", "query" or "cookie"
           name: X-API-KEY  # name of the header, query parameter or cookie

    routerBuilder
      .securityHandler("ApiKeyAuth")
      .bindBlocking(config ->
        APIKeyHandler.create(authProvider)
          .header(config.getString("name")));

Or you can configure more complex scenarios such as OpenId Connect which require server discovery.

    openapi: 3.0.0
    ...
    # 1) Define the security scheme type and attributes
    components:
     securitySchemes:
       openId:   # <--- Arbitrary name for the security scheme. Used to refer to it from elsewhere.
         type: openIdConnect
         openIdConnectUrl: https://example.com/.well-known/openid-configuration

    routerBuilder
      .securityHandler("openId")
      .bind(config ->
        OpenIDConnectAuth
          .discover(vertx, new OAuth2Options()
            .setClientId("client-id") // user provided
            .setClientSecret("client-secret") // user provided
            .setSite(config.getString("openIdConnectUrl")))
          .compose(authProvider -> {
            AuthenticationHandler handler =
              OAuth2AuthHandler.create(vertx, authProvider);
            return Future.succeededFuture(handler);
          }))
      .onSuccess(self -> {
        // Creation completed with success
      })
      .onFailure(err -> {
        // Something went wrong
      });

The API is designed to be fluent so it can be used in a short notation, for example:

    routerBuilder
      .securityHandler("api_key")
      .bindBlocking(config -> APIKeyHandler.create(authProvider))
      .operation("listPetsSingleSecurity")
      .handler(routingContext -> {
        routingContext
          .response()
          .setStatusCode(200)
          .setStatusMessage("Cats and Dogs")
          .end();
      });

    // non-blocking bind
    routerBuilder
      .securityHandler("oauth")
      .bind(config -> OpenIDConnectAuth.discover(vertx, new OAuth2Options(config))
        .compose(oidc -> Future.succeededFuture(
          OAuth2AuthHandler.create(vertx, oidc))))

      .onSuccess(self -> {
        self
          .operation("listPetsSingleSecurity")
          .handler(routingContext -> {
            routingContext
              .response()
              .setStatusCode(200)
              .setStatusMessage("Cats and Dogs")
              .end();
          });
      });

### Blocking vs NonBlocking

From the examples above it is noticeable that handlers can be added in a blocking or not blocking way. The reason for non blocking way usage is not just to support handlers like `OAuth2`. The non-blocking way can be useful for handlers like JWT or basic authentication where the authentication provider requires loading of keys or configuration files.

Here is an example with JWT:

    routerBuilder
      .securityHandler("oauth")
      .bind(config ->
        // as we don't want to block while reading the
        // public key, we use the non blocking bind
        vertx.fileSystem()
          .readFile("public.key")
          // we map the future to a authentication provider
          .map(key ->
            JWTAuth.create(vertx, new JWTAuthOptions()
              .addPubSecKey(new PubSecKeyOptions()
                .setAlgorithm("RS256")
                .setBuffer(key))))
          // and map again to create the final handler
          .map(JWTAuthHandler::create))

      .onSuccess(self ->
        self
          .operation("listPetsSingleSecurity")
          .handler(routingContext -> {
            routingContext
              .response()
              .setStatusCode(200)
              .setStatusMessage("Cats and Dogs")
              .end();
          }));

## Map `AuthenticationHandler` to OpenAPI security schemes

You have seen how you can map an `AuthenticationHandler` to a security schema defined in the contract. The previous examples are validating and will fail your route builder if the configuration is missing.

There could be cases where the contract is incomplete and you explicitly want to define security handlers. In this case the API is slightly different and will not enforce any contract validation. Yet, the security handlers will be available to the builder regardless.

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 `subRouter`:
    Router global = Router.router(vertx);

    Router generated = routerBuilder.createRouter();
    global.route("/v1/*").subRouter(generated);