$ mn create-app my-openapi-app --features openapi
Table of Contents
OpenAPI/Swagger Support
Configuration to integrate Micronaut and OpenAPI/Swagger
Version:
1 Introduction
Micronaut includes support for producing OpenAPI (Swagger) YAML at compilation time. Micronaut will at compile time produce a OpenAPI 3.x compliant YAML file just based on the regular Micronaut annotations and the javadoc comments within your code.
You can customize the generated Swagger using the standard Swagger Annotations.
2 Release History
For this project, you can find a list of releases (with release notes) here:
3 Using the Micronaut CLI
To create a project with OpenAPI/Swagger support using the Micronaut CLI, supply the openapi
feature to the features
flag. For example:
This will create a project with the minimum necessary configuration for OpenAPI.
If you have already created a Micronaut project and will like to add Swagger support, you can simply follow instructions in subsequent sections.
4 Dependencies
To get started add Micronaut’s openapi
to the annotation processor scope of your build configuration:
annotationProcessor("io.micronaut.openapi:micronaut-openapi:3.0.1")
<annotationProcessorPaths>
<path>
<groupId>io.micronaut.openapi</groupId>
<artifactId>micronaut-openapi</artifactId>
<version>3.0.1</version>
</path>
</annotationProcessorPaths>
For Kotlin the openapi dependency should be in the kapt scope and for Groovy in the compileOnly scope.
|
To use the Swagger Annotations add them to the compile classpath
implementation("io.swagger.core.v3:swagger-annotations")
<dependency>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-annotations</artifactId>
</dependency>
5 OpenAPI Definition
Once dependencies have been configured a minimum requirement is to add a @OpenAPIDefinition
annotation to your Application
class:
import io.micronaut.runtime.Micronaut;
import io.swagger.v3.oas.annotations.OpenAPIDefinition;
import io.swagger.v3.oas.annotations.info.Contact;
import io.swagger.v3.oas.annotations.info.Info;
import io.swagger.v3.oas.annotations.info.License;
@OpenAPIDefinition(
info = @Info(
title = "Hello World",
version = "0.0",
description = "My API",
license = @License(name = "Apache 2.0", url = "https://foo.bar"),
contact = @Contact(url = "https://gigantic-server.com", name = "Fred", email = "Fred@gigagantic-server.com")
)
)
public class Application {
public static void main(String[] args) {
Micronaut.run(Application.class);
}
}
import io.swagger.v3.oas.annotations.OpenAPIDefinition
import io.swagger.v3.oas.annotations.info.Contact
import io.swagger.v3.oas.annotations.info.Info
import io.swagger.v3.oas.annotations.info.License
@OpenAPIDefinition(
info = @Info(
title = "Hello World",
version = "0.0",
description = "My API",
license = @License(name = "Apache 2.0", url = "https://foo.bar"),
contact = @Contact(url = "https://gigantic-server.com", name = "Fred", email = "Fred@gigagantic-server.com")
)
)
class Application {
static void main(String[] args) {
Micronaut.run(Application)
}
}
import io.micronaut.runtime.Micronaut
import io.swagger.v3.oas.annotations.OpenAPIDefinition
import io.swagger.v3.oas.annotations.info.Contact
import io.swagger.v3.oas.annotations.info.Info
import io.swagger.v3.oas.annotations.info.License
@OpenAPIDefinition(
info = Info(
title = "Hello World",
version = "0.0",
description = "My API",
license = License(name = "Apache 2.0", url = "https://foo.bar"),
contact = Contact(url = "https://gigantic-server.com", name = "Fred", email = "Fred@gigagantic-server.com")
)
)
object Application {
@JvmStatic
fun main(args: Array<String>) {
Micronaut.run(Application.javaClass)
}
}
With that in place, you compile your project and a OpenAPI YAML file will be generated to the META-INF/swagger
directory of your project’s class output. For example, the above configuration generates:
-
For Java
build/classes/java/main/META-INF/swagger/hello-world-0.0.yml
-
For Kotlin
build/tmp/kapt3/classes/main/META-INF/swagger/hello-world-0.0.yml
The previously defined annotations will produce YAML like the following:
openapi: 3.0.1
info:
title: the title
description: My API
contact:
name: Fred
url: https://gigantic-server.com
email: Fred@gigagantic-server.com
license:
name: Apache 2.0
url: https://foo.bar
version: "0.0"
6 OpenAPI Processing Options
It is possible to tweak the OpenAPI processing with system properties or with a properties file. Options specified with system properties have priority over those defined in the openapi.properties
file.
6.1 Configuring OpenAPI Processing with a properties file
You can specify OpenAPI processing configuration in a file located at the root level of your project directory. The expected filename is openapi.properties
.
It is possible to specify a different location and filename with the micronaut.openapi.config.file
System property.
micronaut.openapi.property.naming.strategy=KEBAB_CASE
micronaut.openapi.target.file=myspecfile.yml
...
..
.
Properties prefixed with micronaut.openapi.expand
will be expanded at compile time, for instance with:
micronaut.openapi.expand.api.version=v1.1
micronaut.openapi.expand.openapi.description=A nice API
The following example shows how to use the previous micronaut.openapi.expand
properties:
@OpenAPIDefinition(
info = @Info(
title = "Hello World",
version = "${api.version}",
description = "${openapi.description}",
license = @License(name = "Apache 2.0", url = "https://foo.bar"),
contact = @Contact(url = "https://gigantic-server.com", name = "Fred", email = "Fred@gigagantic-server.com")
)
)
public class Application {
public static void main(String[] args) {
Micronaut.run(Application.class);
}
}
The generated specification file will look like:
openapi: 3.0.1
info:
title: Hello World
description: A nice API
contact:
name: Fred
url: https://gigantic-server.com
email: Fred@gigagantic-server.com
license:
name: Apache 2.0
url: https://foo.bar
version: "v1.1"
6.2 Configuring OpenAPI Processing with system properties
It is possible to tweak the OpenAPI processing via system properties.
For instance in gradle:
tasks.withType(JavaCompile) {
options.fork = true
options.forkOptions.jvmArgs << '-Dmicronaut.openapi.property.naming.strategy=SNAKE_CASE'
...
}
or in gradle.properties
org.gradle.jvmargs=-Dmicronaut.openapi.property.naming.strategy=SNAKE_CASE
or in maven:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<fork>true</fork>
<compilerArgs>
<arg>-J-Dmicronaut.openapi.property.naming.strategy=SNAKE_CASE</arg>
...
</compilerArgs>
</configuration>
</plugin>
</plugins>
</build>
7 Exposing Swagger Output
If you wish to expose the generated OpenAPI yaml output from your running application you can simply add the necessary static resource configuration to src/main/resources/application.yml
. For example:
micronaut:
router:
static-resources:
swagger:
paths: classpath:META-INF/swagger
mapping: /swagger/**
With the above configuration in place when you run your application you can access your Swagger documentation at http://localhost:8080/swagger/hello-world-0.0.yml
.
8 OpenAPI Generation for Controllers
By default Micronaut will automatically at compile time build out the Swagger YAML definition from your defined controllers and methods. For example given the following class:
import io.micronaut.http.MediaType;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
import io.reactivex.Single;
@Controller("/")
public class HelloController {
/**
* @param name The person's name
* @return The greeting
*/
@Get(uri = "/hello/{name}", produces = MediaType.TEXT_PLAIN)
public Single<String> index(String name) {
return Single.just("Hello " + name + "!");
}
}
import io.micronaut.http.annotation.Controller
import io.micronaut.http.annotation.Get
import io.reactivex.Single
@Controller("/")
class HelloController {
/**
* @param name The person's name
* @return The greeting
*/
@Get(uri = "/hello/{name}", produces = MediaType.TEXT_PLAIN)
Single<String> index(String name) {
return Single.just("Hello $name!")
}
}
import io.micronaut.http.MediaType
import io.micronaut.http.annotation.Controller
import io.micronaut.http.annotation.Get
import io.reactivex.Single
@Controller("/")
open class HelloController {
/**
* @param name The person's name
* @return The greeting
*/
@Get(uri = "/hello/{name}", produces = [MediaType.TEXT_PLAIN])
open fun index(name: String): Single<String> {
return Single.just("Hello $name!")
}
}
The resulting output will be:
openapi: 3.0.1
info:
title: Hello World
description: My API
contact:
name: Fred
url: https://gigantic-server.com
email: Fred@gigagantic-server.com
license:
name: Apache 2.0
url: https://foo.bar
version: "0.0"
paths:
/hello/{name}:
get:
description: ""
operationId: index
parameters:
- name: name
in: path
description: The person's name
required: true
schema:
type: string
responses:
200:
description: The greeting
content:
text/plain:
schema:
type: string
Notice how the javadoc comments are used to fill out the description of the API.
9 Naming Strategy
You can control how the Schema property names are dumped by setting the micronaut.openapi.property.naming.strategy
system property. It accepts one of
the following jackson's PropertyNamingStrategy
:
-
SNAKE_CASE
-
UPPER_CAMEL_CASE
-
LOWER_CAMEL_CASE
-
LOWER_CASE
-
KEBAB_CASE.
10 Swagger Annotations
You can take full control by augmenting your definition with Swagger Annotations. Swagger annotations take precedence over javadoc.
Add the Swagger annotations to the compile classpath
implementation("io.swagger.core.v3:swagger-annotations")
<dependency>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-annotations</artifactId>
</dependency>
and then annotate your controllers:
import io.micronaut.http.MediaType;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
import io.reactivex.Single;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import javax.validation.constraints.NotBlank;
@Controller("/")
public class HelloController {
/**
* @param name The person's name
* @return The greeting message
*/
@Get(uri="/greetings/{name}", produces= MediaType.TEXT_PLAIN)
@Operation(summary = "Greets a person",
description = "A friendly greeting is returned"
)
@ApiResponse(
content = @Content(mediaType = "text/plain",
schema = @Schema(type="string"))
)
@ApiResponse(responseCode = "400", description = "Invalid Name Supplied")
@ApiResponse(responseCode = "404", description = "Person not found")
@Tag(name = "greeting")
public Single<String> greetings(@Parameter(description="The name of the person") @NotBlank String name) {
return Single.just("Hello " + name + ", How are you doing?");
}
}
@Controller("/")
class HelloController {
/**
* @param name The person's name
* @return The greeting message
*/
@Get(uri="/greetings/{name}", produces= MediaType.TEXT_PLAIN)
@Operation(summary = "Greets a person",
description = "A friendly greeting is returned"
)
@ApiResponse(
content = @Content(mediaType = "text/plain",
schema = @Schema(type="string"))
)
@ApiResponse(responseCode = "400", description = "Invalid Name Supplied")
@ApiResponse(responseCode = "404", description = "Person not found")
@Tag(name = "greeting")
Single<String> greetings(@Parameter(description="The name of the person") @NotBlank String name) {
return Single.just("Hello $name, How are you doing?")
}
}
import io.micronaut.http.MediaType
import io.micronaut.http.annotation.Controller
import io.micronaut.http.annotation.Get
import io.reactivex.Single
import io.swagger.v3.oas.annotations.Operation
import io.swagger.v3.oas.annotations.media.Content
import io.swagger.v3.oas.annotations.media.Schema
import io.swagger.v3.oas.annotations.responses.ApiResponse
import io.swagger.v3.oas.annotations.responses.ApiResponses
import io.swagger.v3.oas.annotations.tags.Tag
@Controller("/")
open class HelloController {
/**
* @param name The person's name
* @return The greeting message
*/
@Get(uri = "/greetings/{name}", produces = [MediaType.TEXT_PLAIN])
@Operation(summary = "Greets a person", description = "A friendly greeting is returned")
// Please Note: Repeatable Annotations with non-SOURCE retentions are not yet supported with Kotlin so we are using `@ApiResponses`
// instead of `@ApiResponse`, see https://youtrack.jetbrains.com/issue/KT-12794
@ApiResponses(
ApiResponse(content = [Content(mediaType = "text/plain", schema = Schema(type = "string"))]),
ApiResponse(responseCode = "400", description = "Invalid Name Supplied"),
ApiResponse(responseCode = "404", description = "Person not found")
)
@Tag(name = "greeting")
open fun greetings(name: String): Single<String> {
return Single.just("Hello $name, how are you doing?")
}
}
The resulting output will be:
openapi: 3.0.1
info:
title: Hello World
description: My API
contact:
name: Fred
url: https://gigantic-server.com
email: Fred@gigagantic-server.com
license:
name: Apache 2.0
url: https://foo.bar
version: "0.0"
paths:
/greetings/{name}:
get:
tags:
- greeting
summary: Greets a person
description: A friendly greeting is returned
operationId: greetings
parameters:
- name: name
in: path
description: The name of the person
required: true
schema:
minLength: 1
type: string
responses:
200:
content:
text/plain:
schema:
type: string
400:
description: Invalid Name Supplied
404:
description: Person not found
10.1 Schemas and POJOs
If you return types are not simple strings and primitive types then Micronaut will attempt to generate a Schema definition. You can customize the
generation of the Schema by using the @Schema
annotation on your POJO. For example:
import io.swagger.v3.oas.annotations.media.Schema;
@Schema(name="MyPet", description="Pet description") (1)
class Pet {
private PetType type;
private int age;
private String name;
public void setAge(int a) {
age = a;
}
/**
* The age
*/
@Schema(description="Pet age", maximum="20") (2)
public int getAge() {
return age;
}
public void setName(String n) {
name = n;
}
@Schema(description="Pet name", maxLength=20)
public String getName() {
return name;
}
public void setType(PetType t) {
type = t;
}
public PetType getType() {
return type;
}
}
enum PetType {
DOG, CAT;
}
1 | The @Schema annotation is used to customize the name of the schema |
2 | Properties can be customized too. |
10.2 Schemas and Meta Annotations
If you don’t have control of the source code and don’t want to have to annotate each parameter with @Schema
then it can be convenient to instead use a meta annotation.
For example if the aforementioned Pet
class cannot be annotated with @Schema
you can define a meta annotation:
@Documented
@Retention(RUNTIME)
@Target({ElementType.PARAMETER, ElementType.FIELD})
@Schema(name="MyPet", description="Pet description")
@interface MyAnn {
}
Then whenever Pet
is used as a parameter you can annotate the parameter with @MyAnn
.
10.3 Schemas and Generics
If a method return type includes generics then these will be included when calculating the schema name. For example the following:
class Response<T> {
private T r;
public T getResult() {
return r;
};
}
@Controller("/")
class MyController {
@Put("/")
public Response<Pet> updatePet(Pet pet) {
...
}
}
Will result in a schema called #/components/schemas/Response<Pet>
being generated. If you wish to alter the name of the schema you can do so with the @Schema
annotation:
@Put("/")
@Schema(name="ResponseOfPet")
public Response<Pet> updatePet(Pet pet) {
...
}
In the above case the generated schema will be named #/components/schemas/ResponseOfPet
.
10.4 Schemas naming
By default Micronaut uses Class simple name for Custom type schemas. Micronaut use simple name no matter if @Schema
annotation is defined on type or on property (getter). That means that if you have two properties of same type with @Schema
annotation without name
set, Micronaut will accidentally override one definition with another. So in that case you should
set name
on @Schema
annotation.
import io.swagger.v3.oas.annotations.media.Schema;
@Schema(description="A pet") (1)
class Pet {
}
class Owner {
private Pet bird;
private Pet cat;
private Pet dog;
@Schema(description="Pet that is a a bird") (2)
public Pet getBird() {
return bird;
}
@Schema(description="Pet that is a cat") (3)
public Pet getCat() {
return cat;
}
@Schema(name="Dog", description="Pet that is a dog") (4)
public Pet getDog() {
return cat;
}
}
1 | Micronaut will generate schema with name Pet |
2 | Micronaut will generate schema with name Pet since name is not set, this will conflict with <1> and <3>, final Pet schema might be incorrect |
3 | Micronaut will generate schema with name Pet since name is not set, this will conflict with <1> and <2>, final Pet schema might be incorrect |
4 | Micronaut will generate schema with unique name Dog since name is set, there is no conflict, schema is correctly generated |
10.5 Schemas Annotation resolution
You can apply @Schema
annotation to type or property. But it’s important to note, that Micronaut will prioritize @Schema
on property over @Schema
on type.
import io.swagger.v3.oas.annotations.media.Schema;
@Schema(description="Pet") (1)
class Pet {
}
class Owner {
private Pet cat;
private Pet dog;
public Pet getCat() { (2)
return cat;
}
@Schema(name="MyPet", description="This is my pet") (3)
public Pet getDog() {
return dog;
}
}
1 | Micronaut will detect this annotation |
2 | Micronaut will use annotation <1> from type since there is none on property |
3 | Micronaut will use this annotation even if there exists one on Pet type |
11 Exposing Endpoints
It is possible to expose management Endpoints in the openapi specification file.
11.1 Enable Endpoints
To process user defined endpoints simply add:
endpoints.enabled=true ... .. .
11.2 Endpoints Tags
You can also provide some tags for all endpoints with the endpoints.tags=<comma separated list of tags>
flag, for instance:
endpoints.tags=Management Enpoints
11.3 Micronaut Built-In Endpoints
To enable the processing of built-in endpoints (https://docs.micronaut.io/latest/guide/index.html#providedEndpoints), you have to expose micronaut-management
as annotation processor and runtime dependency:
annotationProcessor("io.micronaut:micronaut-management:3.0.1")
<annotationProcessorPaths>
<path>
<groupId>io.micronaut</groupId>
<artifactId>micronaut-management</artifactId>
<version>3.0.1</version>
</path>
</annotationProcessorPaths>
implementation("io.micronaut:micronaut-management:3.0.1")
<dependency>
<groupId>io.micronaut</groupId>
<artifactId>micronaut-management</artifactId>
<version>3.0.1</version>
</dependency>
and declare them in the openapi.properties file:
endpoints.enabled=true endpoints.tags=Management Endpoints endpoints.routes.class=io.micronaut.management.endpoint.routes.RoutesEndpoint endpoints.beans.class=io.micronaut.management.endpoint.beans.BeansEndpoint endpoints.health.class=io.micronaut.management.endpoint.health.HealthEndpoint endpoints.loggers.class=io.micronaut.management.endpoint.loggers.LoggersEndpoint endpoints.refresh.class=io.micronaut.management.endpoint.refresh.RefreshEndpoint
The syntax is the following: endpoints.<name>.class=<full class name of the endpoint>
where name
is an arbitrary name.
You can also add some tags, servers and security requirements to each endpoint:
endpoints.refresh.class=io.micronaut.management.endpoint.refresh.RefreshEndpoint endpoints.refresh.servers=[{"url": "https://staging.gigantic-server.com/v1", "description": "Staging server"}] endpoints.refresh.security-requirements=[{"petstore_auth": ["write:pets", "read:pets"]}]
11.4 Endpoints Servers
You can also provide some servers for all endpoints with the endpoints.server=<json array of io.swagger.v3.oas.models.servers.Server>
flag, for instance:
endpoints.servers=[ \ { \ "url": "https://{username}.gigantic-server.com:{port}/{basePath}", \ "description": "The production API server", \ "variables": { \ "username": { \ "default": "demo", \ "description": "this value is assigned by the service provider, in this example `gigantic-server.com`" \ }, \ "port": { \ "enum": [ \ "8443", \ "443" \ ], \ "default": "8443" \ }, \ "basePath": { \ "default": "v2" \ } \ } \ } \ ]
11.5 Endpoints Security Requirements
You can also provide some security requirements for all endpoints with the endpoints.security-requirements=<json array of io.swagger.v3.oas.models.security.SecurityRequirement>
flag, for instance:
endpoints.security-requirements=[{"api_key": []}] ... .. .
Don’t forget to declare the referenced SecurityScheme
.
11.6 Endpoints Path
If you are using a custom path for your endpoints use endpoints.path
to set it:
endpoints.path=/endpoints ... .. .
12 Annotation Configuration
Several annotations (OpenAPIInclude OpenAPISecurity OpenAPIManagement) are available to enhance the generated OpenAPI.
To use them add Micronaut’s openapi
to the compile classpath of your application:
implementation("io.micronaut.openapi:micronaut-openapi:3.0.1")
<dependency>
<groupId>io.micronaut.openapi</groupId>
<artifactId>micronaut-openapi</artifactId>
<version>3.0.1</version>
</dependency>
12.1 @OpenAPIInclude
You can use OpenAPIInclude you can include additional Controller or Endpoint already compiled classes in the OpenAPI processing.
@OpenAPIDefinition(
info = @Info(
title = "Hello World",
version = "${api.version}",
description = "${openapi.description}",
license = @License(name = "Apache 2.0", url = "https://foo.bar"),
contact = @Contact(url = "https://gigantic-server.com", name = "Fred", email = "Fred@gigagantic-server.com")
)
)
@OpenAPIInclude(
classes = { io.micronaut.security.endpoints.LoginController.class, io.micronaut.security.endpoints.LogoutController.class },
tags = @Tag(name = "Security")
)
@OpenAPIInclude(
classes = io.micronaut.management.endpoint.env.EnvironmentEndpoint.class,
tags = @Tag(name = "Management"),
security = @SecurityRequirement(name = "BEARER", scopes = {"ADMIN"})
)
public class Application {
public static void main(String[] args) {
Micronaut.run(Application.class);
}
}
12.2 @OpenAPIManagement
OpenAPIManagement adds management endpoints.
OpenAPIManagement is mapped to:
@OpenAPIInclude(
classes = { io.micronaut.management.endpoint.beans.BeansEndpoint.class, io.micronaut.management.endpoint.env.EnvironmentEndpoint.class,
io.micronaut.management.endpoint.health.HealthEndpoint.class,
io.micronaut.management.endpoint.info.InfoEndpoint.class,
io.micronaut.management.endpoint.loggers.LoggersEndpoint.class,
io.micronaut.management.endpoint.refresh.RefreshEndpoint.class,
io.micronaut.management.endpoint.routes.RoutesEndpoint.class,
io.micronaut.management.endpoint.stop.ServerStopEndpoint.class,
io.micronaut.management.endpoint.threads.ThreadDumpEndpoint }
)
12.3 @OpenAPISecurity
OpenAPISecurity adds security endpoints.
It is mapped to:
@OpenAPIInclude(
classes = { io.micronaut.security.endpoints.LoginController.class, io.micronaut.security.endpoints.LogoutController.class }
)
13 Merging Schemas
Often times you might want to generate OpenAPI (Swagger) YAML for built-in endpoints or paths from some other modules, such as security
. In order to generate YAML including all that information, Micronaut supports merging of multiple OpenAPI YAML files. So, you can create OpenAPI YAML files manually at some predefined path from where the information will then be merged into the final YAML file.
For example, if you are using Micronaut’s Security OpenID Connect with Amazon Cognito your application exposes several endpoints which you could define in an external OpenAPI YAML file such as:
openapi: 3.0.1
info:
title: OAuth
description: Endpoints related to the integration with Amazon Cognito
version: "1.0"
paths:
/logout:
get:
tags:
- security
description: deletes the JWT cookie and redirects to /oauth/login/cognito
operationId: logout
parameters: []
responses:
"200":
description: logout 200 response
content:
application/json:
schema:
$ref: '#/components/schemas/Object'
"302":
links:
oauth-login-cognito:
operationId: oauth-login-cognito
/oauth/callback/cognito:
get:
tags:
- security
description: receives a callback from the authorization server with a code to
exchange it for an access and id token
externalDocs:
description: Amazon Cognito Token Endpoint documentation
url: https://docs.aws.amazon.com/cognito/latest/developerguide/token-endpoint.html
operationId: oauth-callback-cognito
parameters:
- name: authorizationResponse
in: query
required: true
explode: true
schema:
$ref: '#/components/schemas/AuthorizationResponse'
responses:
"303":
description: redirects to home page upon successful completion of the authorization
code grant flow
headers:
Set-Cookie:
description: Cookied named JWT with the id token obtained from the authorization
server as the value
/oauth/login/cognito:
get:
tags:
- security
description: redirects to authorization server sign in page
externalDocs:
description: Amazon Cognito Authorization Endpoint documentation
url: https://docs.aws.amazon.com/cognito/latest/developerguide/authorization-endpoint.html
operationId: oauth-login-cognito
parameters: []
responses:
"302":
description: redirects to authorization server sign in page
/oauth/logout:
get:
tags:
- security
description: ends the session in the authorization server and the redirects
to /logout
externalDocs:
description: Amazon Cognito Logout Endpoint documentation
url: https://docs.aws.amazon.com/cognito/latest/developerguide/logout-endpoint.html
operationId: oauth-logout
parameters: []
responses:
"302":
links:
logout:
operationId: logout
components:
schemas:
AuthorizationResponse:
required:
- code
- state
type: object
properties:
code:
required:
- "true"
type: string
description: an authorization code which the OAuth 2.0 client can exchange
for an access token
nonce:
required:
- "false"
type: string
nullable: true
state:
required:
- "true"
type: string
You could also have a yaml file describing the endpoint which exposes the generated OpenAPI YAML file:
openapi: 3.0.1
info:
title: swagger
version: "1.0"
paths:
/swagger/demo-0.0.yml:
get:
tags:
- openapi
description: returns the OpenAPI YAML file describing the API
operationId: swagger
parameters: []
responses:
"200":
description: OpenAPI YAML file describing the API
content:
text/plain: {}
To merge both files with the generated OpenAPI definition point Micronaut to look for additional OpenAPI yaml files in the openapi
folder. You need to set the property micronaut.openapi.additional.files
.
micronaut.openapi.additional.files=openapi
...
..
.
Micronaut will include the endpoints defined in those files in the generated output.
14 Generating OpenAPI Views
Micronaut can generate views for your generated OpenApi specification. Currently Swagger-ui, Redoc and RapiDoc are supported. You can also use RapiPdf to generate a PDF from your spec file.
You can enable multiple views generation in a single application.
The resources needed to render the views (javascript, css, …) are loaded from CDNs: unpkg.com and fonts.googleapis.com.
14.1 Mapping Path
The path from where the swagger specification will be served by the http server defaults to swagger
. You can change it via the mapping.path
property.
Thus, by default, the views expect to find the yaml
under /swagger
.
If you change this mapping to something else:
micronaut:
router:
static-resources:
swagger:
paths: classpath:META-INF/swagger
mapping: /swaggerYAML/**
....
You will need to set the mapping.path
property accordingly: micronaut.openapi.views.spec=mapping.path=swaggerYAML…
.
14.2 Enable Views Generation with a properties file
By default the generation of views is disabled. You can enable views generation with a configuration properties file.
swagger-ui.enabled=true
redoc.enabled=true
rapidoc.enabled=true
rapidoc.bg-color=#14191f
rapidoc.text-color=#aec2e0
rapidoc.sort-endpoints-by=method
...
..
.
14.3 Enable Views Generation with system properties
By default the generation of views is disabled.
To turn it on you have to set the following system property micronaut.openapi.views.spec
.
The string syntax is a series of comma-separated key-value pairs, to enable and configure the views.
micronaut.openapi.views.spec=redoc.enabled=true,rapidoc.enabled=true,swagger-ui.enabled=true,swagger-ui.theme=flattop
For instance in Gradle for Kotlin projects:
JAVA_TOOL_OPTIONS=-Dmicronaut.openapi.views.spec=redoc.enabled=true,rapidoc.enabled=true,swagger-ui.enabled=true,swagger-ui.theme=flattop \
./gradlew --no-daemon clean assemble
or in gradle.properties:
org.gradle.jvmargs=-Dmicronaut.openapi.views.spec=redoc.enabled=true,rapidoc.enabled=true,swagger-ui.enabled=true,swagger-ui.theme=flattop
or in build.gradle as well:
kapt {
arguments {
arg("micronaut.openapi.views.spec", "redoc.enabled=true,rapidoc.enabled=true,swagger-ui.enabled=true,swagger-ui.theme=flattop")
}
}
or in Gradle for Java projects:
tasks.withType(JavaCompile) {
options.fork = true
options.forkOptions.jvmArgs << '-Dmicronaut.openapi.views.spec=rapidoc.enabled=true,swagger-ui.enabled=true,swagger-ui.theme=flattop'
...
}
or in Gradle for Groovy projects:
tasks.withType(GroovyCompile) {
groovyOptions.forkOptions.jvmArgs.add('-Dgroovy.parameters=true')
groovyOptions.forkOptions.jvmArgs.add('-Dmicronaut.openapi.views.spec=rapidoc.enabled=true,swagger-ui.enabled=true,swagger-ui.theme=flattop')
...
}
or in Maven:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<fork>true</fork>
<compilerArgs>
<arg>-J-Dmicronaut.openapi.views.spec=rapidoc.enabled=true,swagger-ui.enabled=true,swagger-ui.theme=flattop</arg>
...
</compilerArgs>
</configuration>
</plugin>
</plugins>
</build>
or in Maven with Groovy:
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>properties-maven-plugin</artifactId>
<version>1.0.0</version>
<executions>
<execution>
<goals>
<goal>set-system-properties</goal>
</goals>
<configuration>
<properties>
<property>
<name>micronaut.openapi.views.spec</name>
<value>rapidoc.enabled=true,swagger-ui.enabled=true,swagger-ui.theme=flattop</value>
</property>
</properties>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
14.4 Swagger-UI
Views supports Swagger UI, to enable it use swagger-ui.enabled=true
.
The views will be generated to the META-INF/swagger/views/swagger-ui
directory of your project’s class output.
|
|
|
String - The version of Swagger-ui to use. Default is to use the latest available. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
See Swagger UI Configuration for a description.
To expose the the swagger-ui
views, you also must expose the generated yaml
:
micronaut:
router:
static-resources:
swagger:
paths: classpath:META-INF/swagger
mapping: /swagger/**
swagger-ui:
paths: classpath:META-INF/swagger/views/swagger-ui
mapping: /swagger-ui/**
With the above configuration in place when you run your application you can access your Swagger documentation at http://localhost:8080/swagger-ui.
14.4.1 Swagger UI - OAuth 2.0 configuration
Swagger UI OAuth 2.0 integration allows you to obtain a token from an authorization server directly from Swagger UI. Then, when you use the Try This
button
in the Swagger UI, the requests issued incorporate a valid token.
You can configure it by setting the following properties:
|
|
|
|
|
|
|
|
|
|
When setting any of those properties, Micronaut will generate not only a swagger-ui/index.html
file, but also a
swagger-ui/oauth2-redirect.html
one. You will need to configure that endpoint as a callback URL in your OAuth 2 Authorization Server.
An example configuration could be:
micronaut.openapi.views.spec=swagger-ui.enabled=true,swagger-ui.theme=flattop,swagger-ui.oauth2RedirectUrl=http://localhost:8080/swagger-ui/oauth2-redirect.html,swagger-ui.oauth2.clientId=myClientId,swagger-ui.oauth2.scopes=openid,swagger-ui.oauth2.usePkceWithAuthorizationCodeGrant=true
Then, you configure the @SecurityScheme
:
@SecurityScheme(name = "openid",
type = SecuritySchemeType.OAUTH2,
scheme = "bearer",
bearerFormat = "jwt",
flows = @OAuthFlows(
authorizationCode = @OAuthFlow(
authorizationUrl = "https://mycompany.okta.com/oauth2/default/v1/authorize",
tokenUrl = "https://mycompany.okta.com/oauth2/default/v1/token",
refreshUrl = "",
scopes = @OAuthScope(name = "openid", description = "OpenID role")
)
)
)
@OpenAPIDefinition(
info = @Info(
title = "API service",
version = "0.0",
description = "My API",
license = @License(name = "Apache 2.0", url = "https://foo.bar"),
contact = @Contact(url = "https://gigantic-server.com", name = "Fred", email = "Fred@gigagantic-server.com")
)
)
public class Application {
public static void main(String[] args) {
Micronaut.run(Application.class, args);
}
}
And the appropriate @SecurityRequirement
on controllers,. eg:
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
import io.micronaut.security.annotation.Secured;
import io.micronaut.security.rules.SecurityRule;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
@Controller
@Secured(SecurityRule.IS_AUTHENTICATED)
public class OrderController {
@Get
@SecurityRequirement(name = "openid", scopes = "openid")
public String index() {
return "Example Response";
}
}
Do not forget to configure Micronaut Security accordingly:
application.yml
micronaut:
security:
enabled: true
token:
jwt:
enabled: true
signatures:
jwks:
okta:
url: 'https://mycompany.okta.com/oauth2/default/v1/keys'
intercept-url-map:
- pattern: /swagger-ui/**
httpMethod: GET
access:
- isAnonymous()
- pattern: /swagger/**
access:
- isAnonymous()
router:
static-resources:
swagger:
paths: classpath:META-INF/swagger
mapping: /swagger/**
swagger-ui:
paths: classpath:META-INF/swagger/views/swagger-ui
mapping: /swagger-ui/**
The previous sample uses a remote Json Web Key Set to validate the token issued by the authorization server. If you use Micronaut Security OpenID Connect support with a server compatible with OpenID Connect Discovery, the JWKS of the authorization server is automatically configured.
14.5 Redoc
Views supports Redoc, to enable it use redoc.enabled=true
.
The views will be generated to the META-INF/swagger/views/redoc
directory of your project’s class output.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
See Redoc Options for a description of the above properites.
To expose the the redoc
views, you also must expose the generated yaml
:
micronaut:
router:
static-resources:
swagger:
paths: classpath:META-INF/swagger
mapping: /swagger/**
redoc:
paths: classpath:META-INF/swagger/views/redoc
mapping: /redoc/**
With the above configuration in place when you run your application you can access your Swagger documentation at http://localhost:8080/redoc.
14.6 RapiDoc
The views will be generated to the META-INF/swagger/views/rapidoc
directory of your project’s class output.
|
|
|
The version of RapiDoc to use. Default is to use the latest available. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
See RapiDoc Options for a description.
To expose the the rapidoc
views, you also must expose the generated yaml
:
micronaut:
router:
static-resources:
swagger:
paths: classpath:META-INF/swagger
mapping: /swagger/**
rapidoc:
paths: classpath:META-INF/swagger/views/rapidoc
mapping: /rapidoc/**
With the above configuration in place when you run your application you can access your Swagger documentation at http://localhost:8080/rapidoc.
14.7 RapiPdf
Views also supports RapiPdf, to enable it use rapipdf.enabled=true
.
RapiPdf supports the following options:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
See RapiPdf Attributes for a description.
It will add a button to the view to generate a PDF from the spec file.
15 Server Context
In micronaut configuration file you can define a server context path (with micronaut.server.context-path
) which serves as a base path for all routes.
Since the yaml specification file and the views are generated at compile time, these resources are not aware of this runtime setting.
It is still possible for the views to work in case a context path is defined:
-
Set
micronaut.openapi.server.context.path
property for compile time resolution. -
Use a
HttpServerFilter
that will add a cookie, or -
Add a parameter to the url.
The view will first look for the cookie and if not present for the parameter.
Compile Time Resolution
Either set micronaut.openapi.server.context.path
as a System Property or in openapi.properties
, then all paths will be prepend with the
specified value at compile time.
If you want the resolution of the context path at runtime use one of the following methods:
HttpServerFilter
Create a HttpServerFilter
that will add a cookie with name contextPath
.
import java.time.Duration;
import org.reactivestreams.Publisher;
import io.micronaut.context.annotation.Requires;
import io.micronaut.context.annotation.Value;
import io.micronaut.core.async.publisher.Publishers;
import io.micronaut.http.HttpMethod;
import io.micronaut.http.HttpRequest;
import io.micronaut.http.MutableHttpResponse;
import io.micronaut.http.annotation.Filter;
import io.micronaut.http.cookie.Cookie;
import io.micronaut.http.filter.HttpServerFilter;
import io.micronaut.http.filter.ServerFilterChain;
@Requires(property = "micronaut.server.context-path")
@Filter(methods = {HttpMethod.GET, HttpMethod.HEAD}, patterns = {"/**/rapidoc*", "/**/redoc*", "/**/swagger-ui*"})
public class OpenApiViewCookieContextPathFilter implements HttpServerFilter {
private final Cookie contextPathCookie;
OpenApiViewCookieContextPathFilter(@Value("${micronaut.server.context-path}") String contextPath) {
this.contextPathCookie = Cookie.of("contextPath", contextPath).maxAge(Duration.ofMinutes(2L));
}
@Override
public Publisher<MutableHttpResponse<?>> doFilter(HttpRequest<?> request, ServerFilterChain chain) {
return Publishers.map(chain.proceed(request), response -> response.cookie(contextPathCookie));
}
}
URL Parameter
Just add a parameter to the view url. For instance if the context path is set to /context/path
you will access your view with http://localhost:8080/context/path/swagger-ui?contextPath=%2Fcontext%2Fpath
.
16 Repository
You can find the source code of this project in this repository: