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:
Abhinav Pandey 2024-01-04 15:35:39 +05:30 committed by GitHub
parent 506c2cedc1
commit 39974e2d6d
13 changed files with 505 additions and 0 deletions

View File

@ -0,0 +1,5 @@
## Micronaut Reactive
This module contains articles about Micronaut Reactive
### Relevant Articles:

View File

@ -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>

View File

@ -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);
}
}

View File

@ -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());
}
}

View File

@ -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");
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -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();
}
}

View File

@ -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

View File

@ -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>

View File

@ -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());
}
}

View File

@ -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>