justineechen / guide-microprofile-openapi

A guide on how to document and filter RESTful APIs from code or static files by using MicroProfile OpenAPI: https://openliberty.io/guides/microprofile-openapi.html

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Documenting RESTful APIs

Note
This repository contains the guide documentation source. To view the guide in published form, view it on the Open Liberty website.

Explore how to document and filter RESTful APIs from code or static files by using MicroProfile OpenAPI.

What you’ll learn

You will learn how to document and filter RESTful APIs from annotations, POJOs, and static OpenAPI files by using MicroProfile OpenAPI.

The OpenAPI specification, previously known as the Swagger specification, defines a standard interface for documenting and exposing RESTful APIs. This specification allows both humans and computers to understand or process the functionalities of services without requiring direct access to underlying source code or documentation. The MicroProfile OpenAPI specification provides a set of Java interfaces and programming models that allow Java developers to natively produce OpenAPI v3 documents from their JAX-RS applications.

You will document the RESTful APIs of the provided inventory service, which serves two endpoints, inventory/systems and inventory/properties. These two endpoints function the same way as in the other MicroProfile guides.

Before you proceed, note that the 1.0 version of the MicroProfile OpenAPI specification does not define how the /openapi endpoint may be partitioned in the event of multiple JAX-RS applications running on the same server. In other words, you must stick to one JAX-RS application per server instance as the behaviour for handling multiple applications is currently undefined.

Try what you’ll build

The finish directory in the root directory of this guide contains the fully documented inventory service. Feel free to give it a try before you proceed.

To try out the service, navigate to the finish directory and then run the Maven install and liberty:start-server goals to build and run the service in Open Liberty:

mvn install liberty:start-server

Next, point your browser to the http://localhost:9080/openapi URL and you’ll see the RESTful APIs of the inventory service. You can also point to the http://localhost:9080/openapi/ui URL for a more interactive view of the deployed APIs. This UI is built from the Open Source Swagger UI and renders the generated /openapi document into a very user friendly page.

When you are done checking out the service and its APIs, stop Open Liberty:

mvn liberty:stop-server

Generating the OpenAPI document for the inventory service

You can generate an OpenAPI document in various ways. First, because all JAX-RS annotations are processed by default, you can augment your existing JAX-RS annotations with OpenAPI annotations to enrich your APIs with a minimal amount of work. Second, you can use a set of predefined models to manually create all elements of the OpenAPI tree. Finally, you can filter various elements of the OpenAPI tree, changing them to your liking or removing them entirely.

Before you proceed, deploy the inventory service to see the bare-bones OpenAPI document that is generated. To deploy the inventory service, navigate to the start directory and run the Maven install phase and the liberty:start-server goal:

mvn install liberty:start-server

The install phase builds the application and packages it into the target/mp-openapi.war file. It also downloads Open Liberty into the target/liberty/wlp directory and configures it to run the application. The liberty:start-server goal starts an Open Liberty server instance.

Because the JAX-RS framework handles basic API generation for JAX-RS annotations, a skeleton OpenAPI tree will be generated from the inventory service. You can use this tree as a starting point and augment it with annotations and code to produce a complete OpenAPI document.

Now, point to the http://localhost:9080/openapi URL to see the generated OpenAPI tree. You can also point to the http://localhost:9080/openapi/ui URL for a more interactive view of the APIs.

Augmenting the existing JAX-RS annotations with OpenAPI annotations

Because all JAX-RS annotations are processed by default, you can augment the existing code with OpenAPI annotations without needing to rewrite portions of the OpenAPI document that are already covered by the JAX-RS framework.

Update the InventoryResource class.
src/main/java/io/openliberty/guides/inventory/InventoryResource.java

Add OpenAPI annotations to the two JAX-RS methods, getPropertiesForHost() and listContents().

InventoryResource.java

link:finish/src/main/java/io/openliberty/guides/inventory/InventoryResource.java[role=include]

Clearly, there are many more annotations now, so let’s break them down:

Annotation Description

@APIResponses

A container for multiple responses from an API operation. This annotation is optional, but it can be helpful to organize a method with multiple responses.

@APIResponse

Describes a single response from an API operation.

@Content

Provides a schema and examples for a particular media type.

@Schema

Defines the input and output data types.

@Operation

Describes a single API operation on a path.

@Parameter

Describes a single operation parameter.

At this point, you can run the the following command to rebuild the application.

mvn compile

Then, refresh the http://localhost:9080/openapi URL to see the updated OpenAPI tree. The two endpoints at which your JAX-RS methods are served are now more meaningful:

/inventory/systems/{hostname}:
  get:
    summary: Get JVM system properties for particular host
    description: Retrieves and returns the JVM system properties from the system
      service running on the particular host.
    operationId: getPropertiesForHost
    parameters:
    - name: hostname
      in: path
      description: The host for whom to retrieve the JVM system properties for.
      required: true
      schema:
        type: string
      example: foo
    responses:
      404:
        description: Invalid hostname or the system service may not be running on
          the particular host.
        content:
          text/plain: {}
      200:
        description: JVM system properties of a particular host.
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Properties'
/inventory/systems:
  get:
    summary: List inventory contents.
    description: Returns the currently stored host:properties pairs in the inventory.
    operationId: listContents
    responses:
      200:
        description: host:properties pairs stored in the inventory.
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/InventoryList'

OpenAPI annotations can also be added to POJOs to describe what they represent. Currently, your OpenAPI document doesn’t have a very meaningful description of the InventoryList POJO and hence it’s very difficult to tell exactly what that POJO is used for. To describe the InventoryList POJO in more detail, augment the src/main/java/io/openliberty/guides/inventory/model/InventoryList.java file with some OpenAPI annotations.

Update the InventoryList class.
src/main/java/io/openliberty/guides/inventory/model/InventoryList.java

Add OpenAPI annotations to the InventoryList class and the systems variable.

InventoryList.java

link:finish/src/main/java/io/openliberty/guides/inventory/model/InventoryList.java[role=include]

Likewise, annotate the src/main/java/io/openliberty/guides/inventory/model/SystemData.java POJO, which is referenced in the InventoryList class.

Update the SystemData class.
src/main/java/io/openliberty/guides/inventory/model/SystemData.java

Add OpenAPI annotations to the SystemData class, the hostname variable and the properties variable.

SystemData.java

link:finish/src/main/java/io/openliberty/guides/inventory/model/SystemData.java[role=include]

Again, run the following command and refresh the http://localhost:9080/openapi URL to see the updated OpenAPI tree.

mvn compile
components:
  schemas:
    InventoryList:
      required:
      - systems
      type: object
      properties:
        systems:
          type: array
          items:
            $ref: '#/components/schemas/SystemData'
        total:
          type: integer
      description: POJO that represents the inventory contents.
    SystemData:
      required:
      - hostname
      - properties
      type: object
      properties:
        hostname:
          type: string
        properties:
          type: object
          additionalProperties:
            type: string
      description: POJO that represents a single inventory entry.
    Properties:
      type: object
      additionalProperties:
        type: string

Filtering the OpenAPI tree elements

Filtering of certain elements and fields of the generated OpenAPI document can be done by using the OASFilter interface.

Create the InventoryOASFilter class.
src/main/java/io/openliberty/guides/inventory/filter/InventoryOASFilter.java

InventoryOASFilter.java

link:finish/src/main/java/io/openliberty/guides/inventory/filter/InventoryOASFilter.java[role=include]

The filterAPIResponse() method allows filtering of APIResponse elements. When you override this method, it will be called once for every APIResponse element in the OpenAPI tree. In this case, you are matching the 404 response that is returned by the /inventory/systems/{hostname} endpoint and setting the previously missing description. To remove an APIResponse element or another filterable element, simply return null.

The filterOpenAPI() method allows filtering of the singleton OpenAPI element. Unlike other filter methods, when you override filterOpenAPI(), it is called only once as the last method for a particular filter. Hence, make sure that it doesn’t override any other filter operations that are called before it. Your current OpenAPI document doesn’t provide much information on the application itself or on what server and port it runs on. This information is usually provided in the info and servers elements, which are currently missing. Use the OASFactory class to manually set these and other elements of the OpenAPI tree from the org.eclipse.microprofile.openapi.models package. The OpenAPI element is the only element that cannot be removed since that would mean removing the whole OpenAPI tree.

Each filtering method is called once for each corresponding element in the model tree. You can think of each method as a callback for various key OpenAPI elements.

Before you can use the filter class that you created, you need to create the microprofile-config.properties file.

Create the configuration file.
src/main/webapp/META-INF/microprofile-config.properties

microprofile-config.properties

link:finish/src/main/webapp/META-INF/microprofile-config.properties[role=include]

This configuration file is picked up automatically by MicroProfile Config and registers your filter by passing in the fully qualified name of the filter class into the mp.openapi.filter property.

Finally, run the following command and refresh the http://localhost:9080/openapi URL to see the updated OpenAPI tree.

mvn compile
info:
  title: Inventory App
  description: App for storing JVM system properties of various hosts.
  license:
    name: Eclipse Public License - v 1.0
    url: https://www.eclipse.org/legal/epl-v10.html
  version: 1.0.0
servers:
- url: http://localhost:{port}
  description: Simple Open Liberty.
  variables:
    port:
      default: "9080"
      description: Server HTTP port.
404:
  description: Invalid hostname or the system service may not be running on
    the particular host.
  content:
    text/plain: {}

For more information about which elements you can filter, see the MicroProfile API.

To learn more about MicroProfile Config, visit the MicroProfile Config GitHub repository and try one of the MicroProfile Config guides.

Using pregenerated OpenAPI documents

As an alternative to generating the OpenAPI model tree from code, you can provide a valid pregenerated OpenAPI document to describe your APIs. This document must be named openapi with a yml, yaml, or json extension and be placed under the META-INF directory. Depending on the scenario, the document might be fully or partially complete. If the document is fully complete, then you can disable annotation scanning entirely by setting the mp.openapi.scan.disable MicroProfile Config property to true. If the document is partially complete, then you can augment it with code.

You can find a complete OpenAPI document in the src/main/webapp/META-INF/openapi.yaml file. This document is the same as your current OpenAPI document with additional APIs for the /inventory/properties endpoint. To make use of this document, open this document in your favorite text editor and uncomment all of its lines.

Update the OpenAPI document file.
src/main/webapp/META-INF/openapi.yaml

Uncomment all the lines in the openapi.yaml file.

openapi.yaml

link:finish/src/main/webapp/META-INF/openapi.yaml[role=include]

Since this document is complete, you can also set the mp.openapi.scan.disable property to true in the src/main/webapp/META-INF/microprofile-config.properties file.

Update the configuration file.
src/main/webapp/META-INF/microprofile-config.properties

Add and set the mp.openapi.scan.disable property to true.

microprofile-config.properties

link:finish/src/main/webapp/META-INF/microprofile-config.properties[role=include]

Run the following command and refresh the http://localhost:9080/openapi URL to see the updated OpenAPI tree.

mvn compile
/inventory/properties:
  get:
    operationId: getProperties
    responses:
      200:
        description: JVM system properties of the host running this service.
        content:
          application/json:
            schema:
              type: object
              additionalProperties:
                type: string

Testing the service

No automated tests are provided to verify the correctness of the generated OpenAPI document. Manually verify the document by visiting the http://localhost:9080/openapi or the http://localhost:9080/openapi/ui URL.

A few tests are included for you to test the basic functionality of the inventory service. If a test failure occurs, then you might have introduced a bug into the code. These tests will run automatically as a part of the Maven build process when you run the following command.

mvn install

You can also run these tests separately from the build by using the following command, but first make sure that the server is stopped.If the server is still running from the previous steps, stop it using the Maven liberty:stop-server goal from command line in the start directory:

mvn liberty:stop-server

Then, verify that the tests pass using the Maven verify goal:

mvn verify

Great work! You’re done!

You have just documented and filtered the APIs of the inventory service from both the code and a static file by using MicroProfile OpenAPI in Open Liberty.

Feel free to try one of the related MicroProfile guides. They demonstrate additional technologies that you can learn and expand on top of what you built here.

For more in-depth examples of MicroProfile OpenAPI, try one of the demo applications available in the MicroProfile OpenAPI GitHub repository.

About

A guide on how to document and filter RESTful APIs from code or static files by using MicroProfile OpenAPI: https://openliberty.io/guides/microprofile-openapi.html

License:Other


Languages

Language:Java 88.9%Language:HTML 11.1%