BAEL-6751 - reactive apis with micronaut (#14824)
* BAEL-6751 - reactive apis with micronaut * BAEL-6751 - reactive apis with micronaut * BAEL-6751 - added error handling * BAEL-6751 - removed unused property * BAEL-6751 - formatting changes * BAEL-6751 - added missing class * BAEL-6751 - moving module to parent pom * BAEL-6751 - moving version number to property
This commit is contained in:
parent
506c2cedc1
commit
39974e2d6d
|
@ -0,0 +1,5 @@
|
|||
## Micronaut Reactive
|
||||
|
||||
This module contains articles about Micronaut Reactive
|
||||
|
||||
### Relevant Articles:
|
|
@ -0,0 +1,199 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>com.baeldung</groupId>
|
||||
<artifactId>micronaut-reactive</artifactId>
|
||||
<version>0.1</version>
|
||||
<packaging>${packaging}</packaging>
|
||||
|
||||
<parent>
|
||||
<groupId>io.micronaut.platform</groupId>
|
||||
<artifactId>micronaut-parent</artifactId>
|
||||
<version>4.1.2</version>
|
||||
</parent>
|
||||
<properties>
|
||||
<packaging>jar</packaging>
|
||||
<jdk.version>17</jdk.version>
|
||||
<release.version>17</release.version>
|
||||
<micronaut.runtime>netty</micronaut.runtime>
|
||||
<micronaut.aot.enabled>false</micronaut.aot.enabled>
|
||||
<micronaut.aot.packageName>com.baeldung.aot.generated</micronaut.aot.packageName>
|
||||
<micronaut.test.resources.enabled>true</micronaut.test.resources.enabled>
|
||||
<exec.mainClass>com.baeldung.micronautreactive.Application</exec.mainClass>
|
||||
<micronaut-test-resources-testcontainers.version>2.1.0</micronaut-test-resources-testcontainers.version>
|
||||
</properties>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>central</id>
|
||||
<url>https://repo.maven.apache.org/maven2</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>io.micronaut</groupId>
|
||||
<artifactId>micronaut-http-server-netty</artifactId>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.micronaut.validation</groupId>
|
||||
<artifactId>micronaut-validation</artifactId>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>jakarta.validation</groupId>
|
||||
<artifactId>jakarta.validation-api</artifactId>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.micronaut.data</groupId>
|
||||
<artifactId>micronaut-data-mongodb</artifactId>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.micronaut.mongodb</groupId>
|
||||
<artifactId>micronaut-mongo-reactive</artifactId>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.micronaut.reactor</groupId>
|
||||
<artifactId>micronaut-reactor</artifactId>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.micronaut.serde</groupId>
|
||||
<artifactId>micronaut-serde-bson</artifactId>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mongodb</groupId>
|
||||
<artifactId>mongodb-driver-reactivestreams</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.micronaut.testresources</groupId>
|
||||
<artifactId>micronaut-test-resources-client</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.micronaut</groupId>
|
||||
<artifactId>micronaut-http-client</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<!-- https://mvnrepository.com/artifact/io.micronaut.testresources/micronaut-test-resources-testcontainers -->
|
||||
<dependency>
|
||||
<groupId>io.micronaut.testresources</groupId>
|
||||
<artifactId>micronaut-test-resources-testcontainers</artifactId>
|
||||
<version>${micronaut-test-resources-testcontainers.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.micronaut.test</groupId>
|
||||
<artifactId>micronaut-test-junit5</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-api</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-engine</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>io.micronaut.maven</groupId>
|
||||
<artifactId>micronaut-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<configFile>aot-${packaging}.properties</configFile>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-enforcer-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<!-- Uncomment to enable incremental compilation -->
|
||||
<!-- <useIncrementalCompilation>false</useIncrementalCompilation> -->
|
||||
|
||||
<annotationProcessorPaths combine.self="override">
|
||||
<path>
|
||||
<groupId>io.micronaut</groupId>
|
||||
<artifactId>micronaut-inject-java</artifactId>
|
||||
<version>${micronaut.core.version}</version>
|
||||
</path>
|
||||
|
||||
<path>
|
||||
<groupId>io.micronaut.data</groupId>
|
||||
<artifactId>micronaut-data-processor</artifactId>
|
||||
<version>${micronaut.data.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>io.micronaut</groupId>
|
||||
<artifactId>micronaut-inject</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</path>
|
||||
<path>
|
||||
<groupId>io.micronaut.data</groupId>
|
||||
<artifactId>micronaut-data-document-processor</artifactId>
|
||||
<version>${micronaut.data.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>io.micronaut</groupId>
|
||||
<artifactId>micronaut-inject</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</path>
|
||||
<path>
|
||||
<groupId>io.micronaut</groupId>
|
||||
<artifactId>micronaut-graal</artifactId>
|
||||
<version>${micronaut.core.version}</version>
|
||||
</path>
|
||||
<path>
|
||||
<groupId>io.micronaut.serde</groupId>
|
||||
<artifactId>micronaut-serde-processor</artifactId>
|
||||
<version>${micronaut.serialization.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>io.micronaut</groupId>
|
||||
<artifactId>micronaut-inject</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</path>
|
||||
<path>
|
||||
<groupId>io.micronaut.validation</groupId>
|
||||
<artifactId>micronaut-validation-processor</artifactId>
|
||||
<version>${micronaut.validation.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>io.micronaut</groupId>
|
||||
<artifactId>micronaut-inject</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</path>
|
||||
</annotationProcessorPaths>
|
||||
<compilerArgs>
|
||||
<arg>-Amicronaut.processing.group=com.baeldung</arg>
|
||||
<arg>-Amicronaut.processing.module=micronaut-reactive</arg>
|
||||
</compilerArgs>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
|
@ -0,0 +1,10 @@
|
|||
package com.baeldung.micronautreactive;
|
||||
|
||||
import io.micronaut.runtime.Micronaut;
|
||||
|
||||
public class Application {
|
||||
|
||||
public static void main(String[] args) {
|
||||
Micronaut.run(Application.class, args);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
package com.baeldung.micronautreactive.controller;
|
||||
|
||||
import com.baeldung.micronautreactive.dtos.BookNotFoundException;
|
||||
import com.baeldung.micronautreactive.entity.Book;
|
||||
import com.baeldung.micronautreactive.service.BookService;
|
||||
import io.micronaut.core.annotation.Nullable;
|
||||
import io.micronaut.http.HttpResponse;
|
||||
import io.micronaut.http.MutableHttpResponse;
|
||||
import io.micronaut.http.annotation.Error;
|
||||
import io.micronaut.http.annotation.*;
|
||||
import jakarta.validation.ConstraintViolationException;
|
||||
import jakarta.validation.Valid;
|
||||
import org.bson.types.ObjectId;
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
@Controller("/books")
|
||||
public class BookController {
|
||||
|
||||
private final BookService bookService;
|
||||
|
||||
public BookController(BookService bookService) {
|
||||
this.bookService = bookService;
|
||||
}
|
||||
|
||||
@Post
|
||||
public String createBook(@Valid @Body Book book) {
|
||||
@Nullable ObjectId bookId = bookService.save(book);
|
||||
if (null == bookId) {
|
||||
return "Book not created";
|
||||
} else {
|
||||
return "Book created with id: " + bookId;
|
||||
}
|
||||
}
|
||||
|
||||
@Put
|
||||
public String updateBook(@Valid @Body Book book) {
|
||||
@Nullable ObjectId bookId = bookService.update(book);
|
||||
if (null == bookId) {
|
||||
return "Book not updated";
|
||||
} else {
|
||||
return "Book updated with id: " + bookId;
|
||||
}
|
||||
}
|
||||
|
||||
@Delete("/{id}")
|
||||
public String deleteBook(String id) throws BookNotFoundException {
|
||||
Long bookId = bookService.deleteById(id);
|
||||
if (1 == bookId) {
|
||||
return "Book deleted";
|
||||
} else {
|
||||
throw new BookNotFoundException(id);
|
||||
}
|
||||
}
|
||||
|
||||
@Get("/{id}")
|
||||
public Book findById(@PathVariable("id") String identifier) throws BookNotFoundException {
|
||||
Book book = bookService.findById(identifier);
|
||||
if (null == book) {
|
||||
throw new BookNotFoundException(identifier);
|
||||
} else {
|
||||
return book;
|
||||
}
|
||||
}
|
||||
|
||||
@Get("/published-after")
|
||||
public Flux<Book> findByYearGreaterThan(@QueryValue(value = "year") int year) {
|
||||
return bookService.findByYearGreaterThan(year);
|
||||
}
|
||||
|
||||
@Error(exception = ConstraintViolationException.class)
|
||||
public MutableHttpResponse<String> onSavedFailed(ConstraintViolationException ex) {
|
||||
return HttpResponse.badRequest(ex.getConstraintViolations().stream()
|
||||
.map(cv -> cv.getPropertyPath() + " " + cv.getMessage())
|
||||
.toList().toString());
|
||||
}
|
||||
|
||||
@Error(exception = BookNotFoundException.class)
|
||||
public HttpResponse<String> onSavedFailed(BookNotFoundException ex) {
|
||||
return HttpResponse.notFound(ex.getMessage());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
package com.baeldung.micronautreactive.dtos;
|
||||
|
||||
public class BookNotFoundException extends Exception {
|
||||
public BookNotFoundException(String id) {
|
||||
super("Book with id " + id + " not found");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
package com.baeldung.micronautreactive.entity;
|
||||
|
||||
import io.micronaut.core.annotation.Introspected;
|
||||
import io.micronaut.serde.annotation.Serdeable;
|
||||
|
||||
@Serdeable
|
||||
public class Author {
|
||||
private String firstName;
|
||||
private String lastName;
|
||||
|
||||
public Author() {
|
||||
}
|
||||
|
||||
public Author(String firstName, String lastName) {
|
||||
this.firstName = firstName;
|
||||
this.lastName = lastName;
|
||||
}
|
||||
|
||||
public String getFirstName() {
|
||||
return firstName;
|
||||
}
|
||||
|
||||
public void setFirstName(String firstName) {
|
||||
this.firstName = firstName;
|
||||
}
|
||||
|
||||
public String getLastName() {
|
||||
return lastName;
|
||||
}
|
||||
|
||||
public void setLastName(String lastName) {
|
||||
this.lastName = lastName;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
package com.baeldung.micronautreactive.entity;
|
||||
|
||||
import io.micronaut.core.annotation.Generated;
|
||||
import io.micronaut.core.annotation.Nullable;
|
||||
import io.micronaut.data.annotation.Id;
|
||||
import io.micronaut.data.annotation.MappedEntity;
|
||||
import io.micronaut.serde.annotation.Serdeable;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import org.bson.types.ObjectId;
|
||||
|
||||
@Serdeable
|
||||
@MappedEntity
|
||||
public class Book {
|
||||
@Id
|
||||
@Generated
|
||||
private @Nullable ObjectId id;
|
||||
@NotBlank
|
||||
private String title;
|
||||
|
||||
@NotNull
|
||||
private Author author;
|
||||
private int year;
|
||||
|
||||
public Book() {
|
||||
}
|
||||
|
||||
public Book(@Nullable ObjectId id, String title, Author author, int year) {
|
||||
this.id = id;
|
||||
this.title = title;
|
||||
this.author = author;
|
||||
this.year = year;
|
||||
}
|
||||
|
||||
public Book(String title, Author author, int year) {
|
||||
this.title = title;
|
||||
this.author = author;
|
||||
this.year = year;
|
||||
}
|
||||
|
||||
public @Nullable ObjectId getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(@Nullable ObjectId id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public Author getAuthor() {
|
||||
return author;
|
||||
}
|
||||
|
||||
public void setAuthor(Author author) {
|
||||
this.author = author;
|
||||
}
|
||||
|
||||
public int getYear() {
|
||||
return year;
|
||||
}
|
||||
|
||||
public void setYear(int year) {
|
||||
this.year = year;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package com.baeldung.micronautreactive.repository;
|
||||
|
||||
import com.baeldung.micronautreactive.entity.Book;
|
||||
import io.micronaut.context.annotation.Parameter;
|
||||
import io.micronaut.core.async.annotation.SingleResult;
|
||||
import io.micronaut.data.mongodb.annotation.MongoFindQuery;
|
||||
import io.micronaut.data.mongodb.annotation.MongoRepository;
|
||||
import io.micronaut.data.repository.reactive.ReactorCrudRepository;
|
||||
import org.bson.types.ObjectId;
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
@MongoRepository
|
||||
public interface BookRepository extends ReactorCrudRepository<Book, ObjectId> {
|
||||
|
||||
@MongoFindQuery("{year: {$gt: :year}}")
|
||||
Flux<Book> findByYearGreaterThan(int year);
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package com.baeldung.micronautreactive.service;
|
||||
|
||||
import com.baeldung.micronautreactive.entity.Book;
|
||||
import com.baeldung.micronautreactive.repository.BookRepository;
|
||||
import jakarta.inject.Singleton;
|
||||
import org.bson.types.ObjectId;
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
@Singleton
|
||||
public class BookService {
|
||||
private final BookRepository bookRepository;
|
||||
|
||||
public BookService(BookRepository bookRepository) {
|
||||
this.bookRepository = bookRepository;
|
||||
}
|
||||
|
||||
public ObjectId save(Book book) {
|
||||
Book savedBook = bookRepository.save(book).block();
|
||||
return null != savedBook ? savedBook.getId() : null;
|
||||
}
|
||||
|
||||
public ObjectId update(Book book) {
|
||||
Book updatedBook = bookRepository.update(book).block();
|
||||
return null != updatedBook ? updatedBook.getId() : null;
|
||||
}
|
||||
|
||||
public Long deleteById(String id) {
|
||||
return bookRepository.deleteById(new ObjectId(id)).block();
|
||||
}
|
||||
|
||||
public Flux<Book> findByYearGreaterThan(int year) {
|
||||
return bookRepository.findByYearGreaterThan(year);
|
||||
}
|
||||
|
||||
public Book findById(String id) {
|
||||
return bookRepository.findById(new ObjectId(id)).block();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
#Sun Aug 27 12:03:55 GMT 2023
|
||||
micronaut.application.name=micronaut-reactive
|
||||
mongodb.uri: mongodb://${MONGO_HOST:localhost}:${MONGO_PORT:27017}/someDb
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
<configuration>
|
||||
|
||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<!-- encoders are assigned the type
|
||||
ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
|
||||
<encoder>
|
||||
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<root level="info">
|
||||
<appender-ref ref="STDOUT" />
|
||||
</root>
|
||||
</configuration>
|
|
@ -0,0 +1,21 @@
|
|||
package com.baeldung.micronautreactive;
|
||||
|
||||
import io.micronaut.runtime.EmbeddedApplication;
|
||||
import io.micronaut.test.extensions.junit5.annotation.MicronautTest;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
|
||||
import jakarta.inject.Inject;
|
||||
|
||||
@MicronautTest(transactional = false)
|
||||
class MicronautReactiveTest {
|
||||
|
||||
@Inject
|
||||
EmbeddedApplication<?> application;
|
||||
|
||||
@Test
|
||||
void testItWorks() {
|
||||
Assertions.assertTrue(application.isRunning());
|
||||
}
|
||||
|
||||
}
|
|
@ -17,6 +17,7 @@
|
|||
<module>helidon</module>
|
||||
<!-- <module>lagom</module> --> <!-- Not a maven project -->
|
||||
<module>micronaut</module>
|
||||
<module>micronaut-reactive</module>
|
||||
<module>microprofile</module>
|
||||
<module>msf4j</module>
|
||||
<module>open-liberty</module>
|
||||
|
|
Loading…
Reference in New Issue