swagger-spring-functional

Validate webflux functional endpoints given a Swagger v2 specification

License

License

Categories

Categories

Swagger Program Interface REST Frameworks
GroupId

GroupId

io.github.cdimascio
ArtifactId

ArtifactId

swagger-spring-functional
Last Version

Last Version

1.0.0
Release Date

Release Date

Type

Type

jar
Description

Description

swagger-spring-functional
Validate webflux functional endpoints given a Swagger v2 specification
Project URL

Project URL

https://github.com/cdimascio/swagger-spring-functional
Project Organization

Project Organization

Carmine DiMascio OSS
Source Code Management

Source Code Management

https://github.com/cdimascio/swagger-spring-functional

Download swagger-spring-functional

How to add to project

<!-- https://jarcasting.com/artifacts/io.github.cdimascio/swagger-spring-functional/ -->
<dependency>
    <groupId>io.github.cdimascio</groupId>
    <artifactId>swagger-spring-functional</artifactId>
    <version>1.0.0</version>
</dependency>
// https://jarcasting.com/artifacts/io.github.cdimascio/swagger-spring-functional/
implementation 'io.github.cdimascio:swagger-spring-functional:1.0.0'
// https://jarcasting.com/artifacts/io.github.cdimascio/swagger-spring-functional/
implementation ("io.github.cdimascio:swagger-spring-functional:1.0.0")
'io.github.cdimascio:swagger-spring-functional:jar:1.0.0'
<dependency org="io.github.cdimascio" name="swagger-spring-functional" rev="1.0.0">
  <artifact name="swagger-spring-functional" type="jar" />
</dependency>
@Grapes(
@Grab(group='io.github.cdimascio', module='swagger-spring-functional', version='1.0.0')
)
libraryDependencies += "io.github.cdimascio" % "swagger-spring-functional" % "1.0.0"
[io.github.cdimascio/swagger-spring-functional "1.0.0"]

Dependencies

compile (5)

Group / Artifact Type Version
com.fasterxml.jackson.module : jackson-module-kotlin jar 2.9.0
com.atlassian.oai : swagger-request-validator-core jar 2.0.2
org.springframework : spring-webflux jar 5.0.2.RELEASE
org.jetbrains.kotlin : kotlin-stdlib jar 1.2.50
org.jetbrains.kotlin : kotlin-reflect jar 1.2.50

test (3)

Group / Artifact Type Version
org.junit.jupiter : junit-jupiter-api jar 5.0.2
org.jetbrains.kotlin : kotlin-test-junit jar 1.2.50
org.springframework : spring-test jar 5.0.2.RELEASE

Project Modules

There are no modules declared in this project.

openapi-spring-webflux-validator

Maven Central Download Codacy Badge All Contributors

A friendly kotlin library to validate API endpoints using an OpenApi 3 or Swagger 2 specification. Great with webflux functional. It works happily with any JVM language including Java >=8.

Supports specifications in YAML and JSON

See this complete Spring 5 Webflux example that uses openapi-spring-webflux-validator.

Prequisites

Java 8 or greater

Install

Maven

<dependency>
    <groupId>io.github.cdimascio</groupId>
    <artifactId>openapi-spring-webflux-validator</artifactId>
    <version>3.3.0</version>
</dependency>

Gradle

compile 'io.github.cdimascio:openapi-spring-webflux-validator:3.3.0'

For sbt, grape, ivy and more, see here

Usage (Kotlin)

This section and the next describe usage with Kotlin and Java respectively.

Configure (Kotlin)

This one-time configuration requires you to provide the location of the openapi/swagger specification and an optional custom error handler.

Supports JSON and YAML

import io.github.cdimascio.openapi.Validate
val validate = Validate.configure("static/api.yaml")

with custom error handler

data class MyError(val id: String, val messages: List<String>)
val validate = Validate.configure("static/api.json") { status, messages ->
   Error(status.name, messages)
}

with custom ObjectMapper factory:

val validate = Validate.configure(
   openApiSwaggerPath = "api.yaml",
   errorHandler = { status, message -> ValidationError(status.value(), message[0]) },
   objectMapperFactory = { ObjectMapper()
       .registerKotlinModule()
       .registerModule(JavaTimeModule())
       .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false) }
)

Validate a request (Kotlin + Reactor)

You can now validate a request in a coroutine style, using the validate instance created above:

without a body

validate.request(req) {
    // Do stuff e.g. return a list of names 
    ok().body(Mono.just(listOf("carmine", "alex", "eliana")))
}

with body

validate.request(req).withBody(User::class.java) { body ->
    // Note that body is deserialized as User!
    // Now you can do stuff. 
    // For example, lets echo the request as the response 
    ok().body(Mono.just(body))
}

Validate a request (Kotlin + coroutines)

Or you can validate a request in a coroutine style, using the validate instance created above:

without a body

validate.requestAndAwait(req) {
    // Do stuff e.g. return a list of names 
    ok().bodyValueAndAwait(listOf("carmine", "alex", "eliana"))
}

with body

validate.request(req).awaitBody(User::class.java) { body: User ->
    // Note that body is deserialized as User!
    // Now you can do stuff. 
    // For example, lets echo the request as the response 
    ok().bodyValueAndAwait(body)
}

Usage (Java 8 or greater)

Configure (Java)

This one-time configuration requires you to provide the location of the openapi/swagger specification and an optional custom error handler.

import io.github.cdimascio.openapi.Validate;
Validate<ValidationError> validate = Validate.configure("static/api.json")

with custom error handler

class MyError {
    private String id;
    private  String messages;
    public MyError(String id, List<String> messages) {
        this.id = id;
        this.messages = messages;
    }
    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }
    public List<String> getMessages() {
        return messages;
    }
    public void setMessages(List<String> messages) {
        this.messages = messages;
    }     
}
Validate<ValidationError> validate = Validate.configure("static/api.json", (status, messages) ->
    new MyError(status.getName(), messages)
);

Validate a request (Java)

Using the validate instance created above, you can now validate a request:

without a body

ArrayList<String> users = new ArrayList<String>() {{
    add("carmine");
    add("alex");
    add("eliana");
}};

validate.request(null, () -> {
    // Do stuff e.g. return a list of user names
    ServerResponse.ok().body(fromObject(users));
});

with body

validate
    .request(null)
    .withBody(User.class, user -> 
        // Note that body is deserialized as User!
        // Now you can do stuff. 
        // For example, lets echo the request as the response
        return ServerResponse.ok().body(fromObject(user))
    );

Example Valiation Output

Let's assume a POST request to create a user requires the following request body:

{
  "firstname": "carmine",
  "lastname": "dimasico"
}

Let's now assume an API user misspells lastname as lastnam

curl -X POST http://localhost:8080/api/users -H "Content-Type: application/json" -d'{ 
  "firstname": "c", 
  "lastnam": "d" 
}'

openapi-spring-webflux-validator automatically validates the request against a Swagger spect and returns:

{
  "code": 400,
  "messages":[
	  "Object instance has properties which are not allowed by the schema: [\"lastnam\"]",
	  "Object has missing required properties ([\"lastname\"])"
  ]
} 

Woah! Cool!! :-D

Example

Let's say you have an endpoint /users that supports both GET and POST operations.

You can create those routes and validate them like so:

Create the routes in a reactive or coroutine style:

package myproject.controllers

import org.springframework.core.io.ClassPathResource
import org.springframework.http.MediaType.*
import org.springframework.web.reactive.function.server.ServerResponse.permanentRedirect
import org.springframework.web.reactive.function.server.coRouter
import org.springframework.web.reactive.function.server.plus
import org.springframework.web.reactive.function.server.router
import java.net.URI

class Routes(private val userHandler: UserHandler) {
    fun router() = router {
        "/api".nest {
            accept(APPLICATION_JSON).nest {
                POST("/users", userHandler::create)
            }
            accept(TEXT_EVENT_STREAM).nest {
                GET("/users", userHandler::findAll)
            }
        }
    } + coRouter { 
        "/coApi".nest {
            accept(APPLICATION_JSON).nest {
                POST("/users", userHandler::coCreate)
            }
            accept(TEXT_EVENT_STREAM).nest {
                GET("/users", userHandler::coFindAll)
            }
        }
    }
}
package myproject

import io.github.cdimascio.openapi.Validate
val validate = Validate.configure("static/api.yaml")

Validate with openapi-spring-webflux-validator

package myproject.controllers

import myproject.models.User
import myproject.validate
import org.springframework.web.reactive.function.server.ServerRequest
import org.springframework.web.reactive.function.server.ServerResponse
import org.springframework.web.reactive.function.server.ServerResponse.ok
import org.springframework.web.reactive.function.server.bodyValueAndAwait
import reactor.core.publisher.Flux
import reactor.core.publisher.Mono

class UserHandler {

    fun findAll(req: ServerRequest): Mono<ServerResponse> {
        return validate.request(req) {
            ok().bodyValue(listOf("carmine", "alex", "eliana"))
        }
    }

    fun create(req: ServerRequest): Mono<ServerResponse> {
        return validate.request(req).withBody(User::class.java) {
            // it is the request body deserialized as User
            ok().bodyValue(it)
        }
    }

    suspend fun coFindAll(req: ServerRequest): ServerResponse {
        return validate.requestAndAwait(req) {
            ok().bodyValueAndAwait(listOf("carmine", "alex", "eliana"))
        }
    }

    suspend fun coCreate(req: ServerRequest): ServerResponse {
        return validate.request(req).awaitBody(User::class.java) {
            // it is the request body deserialized as User
            ok().bodyValueAndAwait(it)
        }
    }
}

License

Apache 2.0

Buy Me A Coffee

Contributors

Thanks goes to these wonderful people (emoji key):


Carmine DiMascio

💻 ⚠️ 📖

Krzysiek Kruczyński

💻 ⚠️ 📖

Chejerla Karthik

💻

Katie Levy

💻

Ilya Builuk

💻

Simon Zambrovski

💻 ⚠️

This project follows the all-contributors specification. Contributions of any kind welcome!

Versions

Version
1.0.0
0.8.3
0.8.2
0.8.1
0.8.0
0.7.1
0.6.2
0.6.1
0.6.0