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