Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[New Docs PR] - Helidon MP OpenAPI #4209 #4421

Merged
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
289 changes: 236 additions & 53 deletions docs/mp/openapi/01_openapi.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,10 @@
:description: Helidon MP OpenAPI Support
:keywords: helidon, mp, microprofile, openapi
:helidon-tag: https://github.com/oracle/helidon/tree/{helidon-version}
:mp-openapi-prefix: https://github.com/eclipse/microprofile-open-api/blob/master
:mp-openapi-spec: {mp-openapi-prefix}/spec/src/main/asciidoc/microprofile-openapi-spec.adoc
:mp-openapi-prefix: https://github.com/eclipse/microprofile-open-api/blob/3.0
:mp-openapi-spec: {mp-openapi-prefix}/spec/src/main/asciidoc/microprofile-openapi-spec.asciidoc
:openapi-spec: https://github.com/OAI/OpenAPI-Specification
:openapi-version: 1.0
:helidon-mp-openapi-example: {helidon-tag}/examples/microprofile/openapi-basic
:jandex-plugin-doc: https://github.com/wildfly/jandex-maven-plugin
:model-reader-java: {mp-openapi-prefix}/api/src/main/java/org/eclipse/microprofile/openapi/OASModelReader.java
Expand All @@ -35,6 +36,19 @@
:microprofile-bundle: true
:common-openapi-doc: ../../shared/openapi/openapi-shared.adoc

== ToC

- <<Overview, Overview>>
- <<Maven Coordinates, Maven Coordinates>>
- <<Usage, Usage>>
- <<API, API>>
- <<Configuration, Configuration>>
- <<Examples, Examples>>
- <<Additional Information, Additional Information>>
- <<Reference, Reference>>

== Overview

Easily allow your Helidon MP application to serve an OpenAPI document
that describes your application's endpoints.

Expand All @@ -55,14 +69,15 @@ include::{common-deps-page-prefix-inc}[tag=maven-dependency]
<1> Defines the MicroProfile OpenAPI annotations so you can use them in your code.
<2> Adds the Helidon MP OpenAPI runtime support.

== Usage

== OpenAPI support in Helidon MP
=== OpenAPI support in Helidon MP

You can very simply add support for OpenAPI to your Helidon MP application. This
document shows what changes you need to make to your application and how to access
the OpenAPI document for your application at runtime.

== Changing your application
=== Changing your application
Helidon MP conforms to the link:{mp-openapi-spec}[MicroProfile OpenAPI specification],
which was inspired by the link:{openapi-spec}[OpenAPI spec] itself.

Expand All @@ -75,51 +90,14 @@ To use OpenAPI from your Helidon MP app:
2. Furnish OpenAPI information about your application's endpoints.
3. Update your application's configuration (optional).

=== Edit your `pom.xml`
==== Building the Jandex index
A Jandex index stores information about the classes and methods in your app and
what annotations they have. It allows CDI to process annotations faster during your
application's start-up.

Add the link:{jandex-plugin-doc}[Jandex maven plug-in] to the `<build><plugins>`
section of your `pom.xml`:

[source,xml,subs="attributes+"]
----
<plugin>
<groupId>org.jboss.jandex</groupId>
<artifactId>jandex-maven-plugin</artifactId>
<version>{jandex-plugin-version}</version>
<executions>
<execution>
<id>make-index</id>
<goals>
<goal>jandex</goal>
</goals>
</execution>
</executions>
</plugin>
----
When you build your app `maven` should include the index `META-INF/jandex.idx` in
the JAR.

[NOTE]
====
If you _do not_ modify your build to create
the index then the Helidon MP OpenAPI runtime automatically creates one in memory during
app start-up. This slows down your app start-up and, depending on how CDI is
configured, might inadvertently miss information.

We _strongly recommend_ using the Jandex plug-in to build the index into your app.
====

=== Furnish OpenAPI information about your endpoints
==== Furnish OpenAPI information about your endpoints
Helidon MP OpenAPI combines information from all of the following sources as it
builds its in-memory model of your application's API. It constructs the OpenAPI
document from this internal model. Your application can use one or more of these
techniques.

==== Annotate the endpoints in your app
===== Annotate the endpoints in your app
You can add MicroProfile OpenAPI annotations to the endpoints in your source code.
These annotations allow the Helidon MP OpenAPI runtime to discover the endpoints
and information about them via CDI at app start-up.
Expand Down Expand Up @@ -147,13 +125,13 @@ This excerpt shows only a few annotations for illustration. The
link:{helidon-mp-openapi-example}[Helidon MP OpenAPI example] illustrates more,
and the link:{mp-openapi-spec}[MicroProfile OpenAPI spec] describes them all.

==== Provide a static OpenAPI file
===== Provide a static OpenAPI file
Add a static file at `META-INF/openapi.yml`, `META-INF/openapi.yaml`,
or `META-INF/openapi.json`. Tools such as Swagger let you describe your app's API
and they then generate an OpenAPI document file which you can include in your application
so OpenAPI can use it.

==== Write and configure a model reader class
===== Write and configure a model reader class
Write a Java class that implements the OpenAPI
link:{model-reader-java}[`org.eclipse.microprofile.openapi.OASModelReader`] interface. Your
model reader code programmatically adds elements to the internal model that OpenAPI
Expand All @@ -162,7 +140,7 @@ builds.
Change your application's MP configuration to set `mp.openapi.model.reader` as the
fully-qualified class name of your class.

==== Write and configure a filter class
===== Write and configure a filter class
Write a Java class that implements the OpenAPI
link:{filter-java}[`org.eclipse.microprofile.openapi.OASFilter`] interface.
As OpenAPI composes its internal model, it invokes your filter with each
Expand All @@ -178,19 +156,224 @@ MP OpenAPI supports a number of other mandated settings. These are described in
link:{mp-openapi-spec}#configuration[configuration section] of the MicroProfile
tjquinno marked this conversation as resolved.
Show resolved Hide resolved
OpenAPI spec.

include::{common-openapi-doc}[common-config]
Assign these settings in `META-INF/microprofile-config.properties`.

== REST Endpoint

== Accessing the OpenAPI document
Now your Helidon MP application will automatically respond to an additional endpoint --
`/openapi` -- and it will return the OpenAPI document describing the endpoints
The Helidon MP application will automatically respond to the built-in endpoint --
`/openapi` -- and it will return the OpenAPI document describing the endpoints
in your application.

By default, per the MicroProfile OpenAPI spec, the default format of the OpenAPI document is YAML.
By default, per the MicroProfile OpenAPI spec, the default format of the OpenAPI document is YAML.
There is not yet an adopted IANA YAML media type, but a proposed one specifically
for OpenAPI documents that has some support is `application/vnd.oai.openapi`.
That is what Helidon returns, by default.

In addition a client can specify the HTTP header `Accept:` as either `application/vnd.oai.openapi+json` or `application/json`
In addition, a client can specify the HTTP header `Accept:` as either `application/vnd.oai.openapi+json` or `application/json`
to request JSON. Alternatively, the client can pass the query parameter `format` as either `JSON` or `YAML` to receive `application/json` or `application/vnd.oai.openapi` (YAML) output, respectively.

== Configuration

Helidon OpenAPI configuration supports the following settings:

include::../../shared/config/io.helidon.microprofile.openapi.MPOpenAPISupport.adoc[]

== Examples

=== Helidon MP Basic OpenAPI Example

This example shows a simple greeting application, similar to the one from the
Helidon MP QuickStart, enhanced with OpenAPI support.

[source,java]
----
@Path("/greeting")
@PUT
@Operation(summary = "Set the greeting prefix",
description = "Permits the client to set the prefix part of the greeting (\"Hello\")") //<1>
@RequestBody( //<2>
name = "greeting",
description = "Conveys the new greeting prefix to use in building greetings",
content = @Content(
mediaType = "application/json",
schema = @Schema(implementation = GreetingMessage.class),
examples = @ExampleObject(
name = "greeting",
summary = "Example greeting message to update",
value = "New greeting message")))
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response updateGreeting(JsonObject jsonObject) {
...
}
----
<1> With `@Operation` annotation we document the current method.
<2> With `@RequestBody` annotation we document the content produced. Internal annotations `@Content`, `@Schema` and `@ExampleObjects` are used to give more details about the returned data.

If we want to hide a specific path an `OASFilter` is used.

The OASFilter interface allows application developers to receive callbacks for various key OpenAPI elements. The interface has a default implementation for every method, which allows application developers to only override the methods they care about. To use it, simply create an implementation of this interface and register it using the `mp.openapi.filter configuration` key, where the value is the fully qualified name of the filter class.

The following example filter prevents information about a given path from appearing in the OpenAPI document.

[source, java]
----
import org.eclipse.microprofile.openapi.models.Operation;
import org.eclipse.microprofile.openapi.models.PathItem;

public class SimpleAPIFilter implements OASFilter {

@Override
public PathItem filterPathItem(PathItem pathItem) {
for (Map.Entry<PathItem.HttpMethod, Operation> methodOp
: pathItem.getOperations().entrySet()) {
if (SimpleAPIModelReader.DOOMED_OPERATION_ID
.equals(methodOp.getValue().getOperationId())) {
return null;
}
}
return OASFilter.super.filterPathItem(pathItem);
}
}
----

You can implement a model reader to provide all or part of the in-memory `OpenAPI` model programmatically. Helidon `OpenAPI` merges the model from the model reader with models from the other sources (a static file and annotations). The example model reader below creates an `OpenAPI` object describing two paths. It turns out that the filter described earlier will suppress one of the paths, but the model reader does not know or care.

[source,java]
----
import org.eclipse.microprofile.openapi.OASFactory;
import org.eclipse.microprofile.openapi.OASModelReader;
import org.eclipse.microprofile.openapi.models.OpenAPI;
import org.eclipse.microprofile.openapi.models.PathItem;
import org.eclipse.microprofile.openapi.models.Paths;

/**
* Defines two paths using the OpenAPI model reader mechanism, one that should
* be suppressed by the filter class and one that should appear in the published
* OpenAPI document.
*/
public class SimpleAPIModelReader implements OASModelReader {

/**
* Path for the example endpoint added by this model reader that should be visible.
*/
public static final String MODEL_READER_PATH = "/test/newpath";

/**
* Path for an endpoint that the filter should hide.
*/
public static final String DOOMED_PATH = "/test/doomed";

/**
* ID for an endpoint that the filter should hide.
*/
public static final String DOOMED_OPERATION_ID = "doomedPath";

/**
* Summary text for the endpoint.
*/
public static final String SUMMARY = "A sample test endpoint from ModelReader";

@Override
public OpenAPI buildModel() {
/*
* Add two path items, one of which we expect to be removed by
* the filter and a very simple one that will appear in the
* published OpenAPI document.
*/
PathItem newPathItem = OASFactory.createPathItem()
.GET(OASFactory.createOperation()
.operationId("newPath")
.summary(SUMMARY));
PathItem doomedPathItem = OASFactory.createPathItem()
.GET(OASFactory.createOperation()
.operationId(DOOMED_OPERATION_ID)
.summary("This should become invisible"));
OpenAPI openAPI = OASFactory.createOpenAPI();
Paths paths = OASFactory.createPaths()
.addPathItem(MODEL_READER_PATH, newPathItem)
.addPathItem(DOOMED_PATH, doomedPathItem);
openAPI.paths(paths);

return openAPI;
}
}
----

Having written the filter and model reader classes, identify them by adding configuration to `META-INF/microprofile-config.properties` as the following example shows.

[source,properties]
----
mp.openapi.filter=io.helidon.microprofile.examples.openapi.basic.internal.SimpleAPIFilter
mp.openapi.model.reader=io.helidon.microprofile.examples.openapi.basic.internal.SimpleAPIModelReader
----


Now just build and run:

[source,bash]
----
mvn package
java -jar target/helidon-examples-microprofile-openapi-basic.jar
----

Try the endpoints:

[source,bash]
----
curl -X GET http://localhost:8080/greet
{"message":"Hello World!"}

curl -X GET http://localhost:8080/openapi
[lengthy OpenAPI document]
----

The output describes not only then endpoints from `GreetResource` but
also one contributed by the `SimpleAPIModelReader`.

Full example is available link:https://github.com/oracle/helidon/tree/{health-release}/examples/microprofile/openapi-basic[in our official repository]


== Additional information

=== Building the Jandex index

A Jandex index stores information about the classes and methods in your app and
what annotations they have. It allows CDI to process annotations faster during your
application's start-up.

Add the link:{jandex-plugin-doc}[Jandex maven plug-in] to the `<build><plugins>`
section of your `pom.xml`:

[source,xml,subs="attributes+"]
----
<plugin>
<groupId>org.jboss.jandex</groupId>
<artifactId>jandex-maven-plugin</artifactId>
<version>{jandex-plugin-version}</version>
<executions>
<execution>
<id>make-index</id>
<goals>
<goal>jandex</goal>
</goals>
</execution>
</executions>
</plugin>
----
When you build your app `maven` should include the index `META-INF/jandex.idx` in
the JAR.

[NOTE]
====
If you _do not_ modify your build to create
the index then the Helidon MP OpenAPI runtime automatically creates one in memory during
app start-up. This slows down your app start-up and, depending on how CDI is
configured, might inadvertently miss information.

We _strongly recommend_ using the Jandex plug-in to build the index into your app.
====

== Reference

* link:https://github.com/eclipse/microprofile-open-api[MicroProfile OpenAPI GitHub Repository]
* link:https://download.eclipse.org/microprofile/microprofile-open-api-{:openapi-version:}/microprofile-openapi-spec.pdf[MicroProfile OpenAPI Specification]