SpringDoc with Spring Webflux functional endpoints - spring

I'm using Spring Webfluxs' functional endpoints and would like to generate an openAPI definition for it.
I added SpringDoc, but the resulting openAPI definition is empty:
{
"openapi": "3.0.1",
"info": {
"title": "OpenAPI definition",
"version": "v0"
},
"servers": [
{
"url": "http://localhost:8080",
"description": "Generated server url"
}
],
"paths": {},
"components": {}
}
Endpoints are defined like
#Configuration(proxyBeanMethods = false)
public class Routers {
#Bean
public RouterFunction<ServerResponse> uploadRoute(UploadHandler uploadHandler) {
return RouterFunctions
.route(POST("/api/upload").and(accept(MediaType.MULTIPART_FORM_DATA)), uploadHandler::handleUploadedFiles);
}
}
Is it possible to derive an openAPI definition from Springs' functional endpoints?

You can either use, functional DSL or the #RouterOperations annotations.
More details are available in this section of springdoc-openapi documentation.
This sample code shows is a demo code, for how functional endpoints are supported:
https://github.com/springdoc/springdoc-openapi-demos/tree/master/springdoc-openapi-spring-boot-2-webflux-functional

Related

Spring boot - health endpoint - can I have more than one URI, one simple, other more detailed?

I wanted to have two health-related endpoints in my spring boot application one simple and other one in more detailed, for example -
the simple API -
GET http://localhost:8080/actuator/health
{
"status": "UP"
}
the detailed API -
GET http://localhost:8080/actuator/health-detailed
{
"status": "UP",
"components": {
"custom": {
"status": "UP"
},
"diskSpace": {
"status": "UP",
"details": {
"total": 254971625472,
"free": 60132696064,
"threshold": 10485760,
"exists": true
}
},
"ping": {
"status": "UP"
}
}
}
But I am not able to figure out how to achieve this with Spring boot actuator.
Yes you can have.
Health Indicators in Spring Boot
Out of the box, Spring Boot registers many HealthIndicators to report the healthiness of a particular application aspect.
Some of those indicators are almost always registered, such as DiskSpaceHealthIndicator or PingHealthIndicator. The former reports the current state of the disk and the latter serves as a ping endpoint for the application.
On the other hand, Spring Boot registers some indicators conditionally. That is if some dependencies are on the classpath or some other conditions are met, Spring Boot might register a few other HealthIndicators, too. For instance, if we're using relational databases, then Spring Boot registers DataSourceHealthIndicator. Similarly, it'll register CassandraHealthIndicator if we happen to use Cassandra as our data store.
In order to inspect the health status of a Spring Boot application, we can call the /actuator/health endpoint. This endpoint will report an aggregated result of all registered HealthIndicators.
Custom HealthIndicators
In addition to the built-in ones, we can register custom HealthIndicators to report the health of a component or subsystem. In order to that, all we have to do is to register an implementation of the HealthIndicator interface as a Spring bean.
#Component
public class RandomHealthIndicator implements HealthIndicator {
#Override
public Health health() {}
}
add this to your pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
and then create this class to add your custom health checks:
#Component
public class CustomHealthCheck extends AbstractHealthIndicator {
#Override
protected void doHealthCheck(Health.Builder bldr) throws Exception {
// TODO implement some check
boolean running = true;
if (running) {
bldr.up();
} else {
bldr.down();
}
}
}
and first status in response of /actuator/health indicates brief status that you want.
{
"status": "UP",
"components": {
"db": {
"status": "UP",
"details": {
"database": "Oracle",
"validationQuery": "isValid()"
}
},
"diskSpace": {
"status": "UP",
"details": {
"total": 268238319616,
"free": 40618704896,
"threshold": 10485760,
"exists": true
}
}
}

How to prevent spring hateoas to return empty links?

I am generating response classes that could provide links.
I'd like to avoid the links attribute to show in case no link is provided, as for example now I'm getting:
{
"links": [], <--not the best
"id": 1,
"category": {
"links": [],
"id": 3
}
I would instead like to have:
{
"id": 1,
"category": {
"id": 3
},
I tried defining a global mapper:
#Configuration
public class JacksonConfig {
#Autowired
private ObjectMapper objectMapper; //reuse the pre-configured mapper
#PostConstruct
public void setup() {
objectMapper.enable(SerializationFeature.INDENT_OUTPUT);
objectMapper.setDefaultPropertyInclusion(JsonInclude.Include.NON_EMPTY);
}
}
And for every other dto I have it works...except for the hateoas links!
What am I doing wrong?
edit:
I think I made a step further. It only happens when the RepresentationModel is in a List:
#GetMapping("/pet")
public ResponseEntity<List<Pet>> getPets(){ <-- this returns empty links
#GetMapping("/pet")
public ResponseEntity<Pet> getPet(){ <-- this doesn't

Intellij different behavior depends on deployed war exploded or not

I see strange behavior in Intellij and JBoss server.
Short info about application:
- spring 4.3.1
- jersey 1.18.1
- jboss EAP 6.4.0
I have an issue with marshaling, so I tried to dig deeper and try hints from: https://docs.jboss.org/resteasy/docs/1.0.0.GA/userguide/html/Content_Marshalling_Providers.html
So I created class:
#Named
#Provider
#Primary
#Produces(MediaType.APPLICATION_JSON)
public class JerseyJAXBConfiguration implements ContextResolver<JAXBContext> {
private JAXBContext context;
private Class[] types = { SomeClass.class};
public JerseyJAXBConfiguration() throws Exception {
JSONConfiguration.MappedBuilder builder = JSONConfiguration.mapped();
builder.arrays("invite");
builder.rootUnwrapping(false);
this.context = new JSONJAXBContext(builder.build(), types);
}
public JAXBContext getContext(Class<?> objectType) {
for (Class type : types) {
if (type == objectType) {
return context;
}
}
return null;
}
}
And I have two JBoss run configurations in IntelliJ. First that deploy simple 'war' and second that deploy 'war exploded'. And here is a strange thing. Because in both cases this bean is loaded when the application is starting, but only for a case without exploded 'getContext' method is executed. In both cases, everything is the same. Nothing in code is changed between runs.
Can someone explain to me why we have this different? I would like to know what JAXB implementation is executed in a case for 'war exploded'.
The main reason why I looking at it is because Json that is created by web service also is different in metioned cases. On have "named" array second don't have.
In case 'war':
{"functionGroupAutoFill": [
{
"desc": "0. General",
"id": "0"
},
{
"desc": "00. General",
"id": "00"
},
...
]}
In case 'war exploded':
[
{
"desc": "0. General",
"id": "0"
},
{
"desc": "00. General",
"id": "00"
},
...
]

Spring Cloud Contract with Jersey

I have a simple project Spring boot project. It contains one Jersey based Controller:
#Path("persons")
#Produces(MediaType.APPLICATION_JSON)
public class PersonsController {
#GET
public Person get() {
return new Person("James", 20);
}
}
It returns json response as expected (url: http://localhost:PORT/persons):
{
"name": "James",
"age": 20
}
My aim is to add Spring Cloud Contract tests for this controller. I have added all required mvn configurations, and test:
public class MvcTest {
#Before
public void setup() {
RestAssuredMockMvc.standaloneSetup(new PersonsController());
}
}
Here is contract (groovy file):
import org.springframework.cloud.contract.spec.Contract
Contract.make {
request {
method 'GET'
url('persons')
}
response {
status 200
body(
"name": "James",
"age": 20
)
}
}
When I run mvn clean package following error always is returned:
Failed tests:
ContractVerifierTest.validate_getTest:26 expected:<[200]> but was:<[404]>
I believe this should be related to the ServletDispatcher as it doesn't see Jersey's paths. The same project with replaced #Path to #RequestMapping works. However, I need to make it working with Jersey.
Have I missed something?
Have you checked the section about jaxrs support? https://cloud.spring.io/spring-cloud-contract/1.0.x/spring-cloud-contract.html#_jax_rs_support. Here you have an example how you can use it https://github.com/spring-cloud/spring-cloud-contract/tree/1.0.x/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject

How to remove the "_embedded" property in Spring HATEOAS

I'm using Spring Boot and HATEOAS to build a REST API and when my API returns a collection, it is wrapped inside a "_embedded" property, like so:
{
"_links":{
"self":{
"href":"http://localhost:8080/technologies"
}
},
"_embedded":{
"technologies":[
{
"id":1,
"description":"A",
"_links":{
"self":{
"href":"http://localhost:8080/technologies/1"
}
}
},
{
"id":2,
"description":"B",
"_links":{
"self":{
"href":"http://localhost:8080/technologies/2"
}
}
}
]
}
}
I want the response to be like this:
{
"_links":{
"self":{
"href":"http://localhost:8080/technologies"
}
},
"technologies":[
{
"id":1,
"description":"A",
"_links":{
"self":{
"href":"http://localhost:8080/technologies/1"
}
}
},
{
"id":2,
"description":"B",
"_links":{
"self":{
"href":"http://localhost:8080/technologies/2"
}
}
}
]
}
My TechnologiesController:
#RestController
#ExposesResourceFor(Technology.class)
#RequestMapping(value = "/technologies")
public class TechnologiesController {
...
#ResquestMapping(method = RequestMethod.GET, produces = "application/vnd.xpto-technologies.text+json")
public Resources<Resource<Technology>> getAllTechnologies() {
List<Technology> technologies = technologyGateway.getAllTechnologies();
Resources<<Resource<Technology>> resources = new Resources<Resource<Technology>>(technologyResourceAssembler.toResources(technologies));
resources.add(linkTo(methodOn(TechnologiesController.class).getAllTechnologies()).withSelfRel());
return resources;
}
The configuration class has the annotation #EnableHypermediaSupport(type = EnableHypermediaSupport.HypermediaType.HAL).
What is the best way to produce the response without the "_embedded"?
As the documentation says
application/hal+json responses should be sent to requests that accept
application/json
In order to omit _embedded in you response you'll need to add
spring.hateoas.use-hal-as-default-json-media-type=false
to application.properties.
I close HAL feature, because it is hard to using Resources/Resource by restTemplate. I disable this feature by following code:
public class SpringRestConfiguration implements RepositoryRestConfigurer {
#Override
public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
config.setDefaultMediaType(MediaType.APPLICATION_JSON);
config.useHalAsDefaultJsonMediaType(false);
}
}
It work for me. HAL is good if there are more support with restTemplate.
Adding this Accept header to the request:
Accept : application/x-spring-data-verbose+json
For those who use Spring Data, and consider it as a problem - solution is to set
spring.data.rest.defaultMediaType = application/json
in application properties.
There still links will be available, but no _embedded any more.
What you're describing in the produced and expected results are semantically different things. The former thing is the HAL representation of a Collection<Technology>. The latter, which you expect is the representation of:
class Wrapper {
Resources<Technology> technologies;
}
Note how this is how we actually create the top level technologies property that you would like to see in your response. You don't create any of the latter in your controller. A top-level Resourcesinstance is basically a collection and the only way to represent a top-level collection in HAL is _embedded. Apparently you don't want that but that's what you have written in your controller method.
Assuming you have Wrapper, something like this should work (untested):
Wrapper wrapper = new Wrapper(assembler.toCollectionModel(technologies);
EntityModel<Wrapper> model = EntityModel.of(wrapper);
model.add(linkTo(…));
PS: As of Spring HATEOAS 1.0, Resources is CollectionModel and Resourceis EntityModel.
You can use this code in the service
constructor(
private httpClient: HttpClient
) { }
retrieveAllStudents(){
return this.httpClient.get<any[]>(`http://localhost:8080/students`);
}
This will deal with the _embedded part of Json and extract the desired data.
export class ListStudentsComponent implements OnInit {
// declaring variables to be used
student: Student;
students: Student[];
message: string;
// injecting student service into the constuctor
constructor(
private studentService: StudentService,
) { }
ngOnInit() {
this.refreshStudents();
}
refreshStudents(){
this.studentService.retrieveAllStudents().subscribe(
response => {
console.log(response);
this.students = response._embedded.students as Student[];
}
);
}
For latest versions in Spring RepositoryRestConfigurer doesn't include the method public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) you'd need to override the default method on RepositoryRestConfigurer which include cors parameter.
public class RestConfiguration implements RepositoryRestConfigurer {
public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config, CorsRegistry cors) {
config.setDefaultMediaType(MediaType.APPLICATION_JSON);
config.useHalAsDefaultJsonMediaType(false);
}
}

Resources