diff --git a/.github/labeler.yml b/.github/labeler.yml
index d353f5e7a..0cf4a8174 100644
--- a/.github/labeler.yml
+++ b/.github/labeler.yml
@@ -99,6 +99,7 @@
- any-glob-to-any-file:
- r2dbc/boot-jooq-r2dbc-sample/**/*
- r2dbc/boot-r2dbc-sample/**/*
+ - r2dbc/boot-reactive-cache/**/*
"component: jobrunr":
- changed-files:
- any-glob-to-any-file:
@@ -150,6 +151,7 @@
- open-api-spring-boot/pom.xml
- r2dbc/boot-jooq-r2dbc-sample/pom.xml
- r2dbc/boot-r2dbc-sample/pom.xml
+ - r2dbc/boot-reactive-cache/pom.xml
- scheduler/boot-jobrunr-sample/pom.xml
- scheduler/boot-scheduler-quartz/pom.xml
- scheduler/boot-shedlock-sample/pom.xml
diff --git a/r2dbc/boot-reactive-cache/pom.xml b/r2dbc/boot-reactive-cache/pom.xml
index ada2f2519..c29670e33 100644
--- a/r2dbc/boot-reactive-cache/pom.xml
+++ b/r2dbc/boot-reactive-cache/pom.xml
@@ -56,6 +56,7 @@
org.springframework.boot
spring-boot-starter-webflux
+
org.springframework.boot
spring-boot-devtools
@@ -78,10 +79,15 @@
org.springframework.boot
spring-boot-starter-data-redis-reactive
+
+ org.apache.commons
+ commons-pool2
+
org.springframework.boot
spring-boot-starter-data-r2dbc
+
org.postgresql
postgresql
@@ -92,6 +98,7 @@
r2dbc-postgresql
runtime
+
org.flywaydb
flyway-core
diff --git a/r2dbc/boot-reactive-cache/src/main/java/com/example/cache/services/MovieService.java b/r2dbc/boot-reactive-cache/src/main/java/com/example/cache/services/MovieService.java
index d2f30115e..11e06a2de 100644
--- a/r2dbc/boot-reactive-cache/src/main/java/com/example/cache/services/MovieService.java
+++ b/r2dbc/boot-reactive-cache/src/main/java/com/example/cache/services/MovieService.java
@@ -5,6 +5,7 @@
import com.example.cache.model.request.MovieRequest;
import com.example.cache.model.response.MovieResponse;
import com.example.cache.repositories.MovieRepository;
+import com.example.cache.utils.AppConstants;
import org.springframework.data.redis.core.ReactiveRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -37,22 +38,25 @@ public Flux findAll() {
.switchIfEmpty(movieRepository
.findAll()
// Persisting the fetched movies in the cache.
- .flatMap(movie -> reactiveRedisTemplate.opsForValue().set("movie:" + movie.id(), movie))
+ .flatMap(movie ->
+ reactiveRedisTemplate.opsForValue().set(AppConstants.MOVIE_KEY + movie.id(), movie))
// Fetching the movies from the updated cache.
- .thenMany(reactiveRedisTemplate.keys("movie:*").flatMap(key -> reactiveRedisTemplate
- .opsForValue()
- .get(key))))
+ .thenMany(reactiveRedisTemplate
+ .keys(AppConstants.MOVIE_KEY + "*")
+ .flatMap(key ->
+ reactiveRedisTemplate.opsForValue().get(key))))
.map(movieMapper::toResponse);
}
public Mono findMovieById(Long id) {
return reactiveRedisTemplate
.opsForValue()
- .get("movie:" + id)
+ .get(AppConstants.MOVIE_KEY + id)
.switchIfEmpty(movieRepository
.findById(id)
- .flatMap(movie -> reactiveRedisTemplate.opsForValue().set("movie:" + movie.id(), movie))
- .then(reactiveRedisTemplate.opsForValue().get("movie:" + id)))
+ .flatMap(movie ->
+ reactiveRedisTemplate.opsForValue().set(AppConstants.MOVIE_KEY + movie.id(), movie))
+ .then(reactiveRedisTemplate.opsForValue().get(AppConstants.MOVIE_KEY + id)))
.map(movieMapper::toResponse);
}
@@ -62,8 +66,8 @@ public Mono saveMovie(MovieRequest movieRequest) {
.flatMap(movieRepository::save)
.flatMap(movie -> reactiveRedisTemplate
.opsForValue()
- .set("movie:" + movie.id(), movie)
- .then(reactiveRedisTemplate.opsForValue().get("movie:" + movie.id())))
+ .set(AppConstants.MOVIE_KEY + movie.id(), movie)
+ .then(reactiveRedisTemplate.opsForValue().get(AppConstants.MOVIE_KEY + movie.id())))
.map(movieMapper::toResponse);
}
@@ -75,13 +79,13 @@ public Mono updateMovie(Long id, MovieRequest movieRequest) {
.flatMap(movieRepository::save)
.flatMap(movie -> reactiveRedisTemplate
.opsForValue()
- .set("movie:" + movie.id(), movie)
- .then(reactiveRedisTemplate.opsForValue().get("movie:" + movie.id())))
+ .set(AppConstants.MOVIE_KEY + movie.id(), movie)
+ .then(reactiveRedisTemplate.opsForValue().get(AppConstants.MOVIE_KEY + movie.id())))
.map(movieMapper::toResponse);
}
@Transactional
public Mono deleteMovieById(Long id) {
- return movieRepository.deleteById(id).then(reactiveRedisTemplate.delete("movie:" + id));
+ return movieRepository.deleteById(id).then(reactiveRedisTemplate.delete(AppConstants.MOVIE_KEY + id));
}
}
diff --git a/r2dbc/boot-reactive-cache/src/main/java/com/example/cache/utils/AppConstants.java b/r2dbc/boot-reactive-cache/src/main/java/com/example/cache/utils/AppConstants.java
index 9d618c5d7..c8f425133 100644
--- a/r2dbc/boot-reactive-cache/src/main/java/com/example/cache/utils/AppConstants.java
+++ b/r2dbc/boot-reactive-cache/src/main/java/com/example/cache/utils/AppConstants.java
@@ -1,12 +1,8 @@
package com.example.cache.utils;
public final class AppConstants {
- public static final String PROFILE_PROD = "prod";
- public static final String PROFILE_NOT_PROD = "!" + PROFILE_PROD;
public static final String PROFILE_TEST = "test";
- public static final String PROFILE_NOT_TEST = "!" + PROFILE_TEST;
- public static final String DEFAULT_PAGE_NUMBER = "0";
- public static final String DEFAULT_PAGE_SIZE = "10";
- public static final String DEFAULT_SORT_BY = "id";
- public static final String DEFAULT_SORT_DIRECTION = "asc";
+ public static final String MOVIE_KEY = "movie:";
+ private static final String PROFILE_PROD = "prod";
+ public static final String PROFILE_NOT_PROD = "!" + PROFILE_PROD;
}
diff --git a/r2dbc/boot-reactive-cache/src/test/java/com/example/cache/web/controllers/MovieControllerIT.java b/r2dbc/boot-reactive-cache/src/test/java/com/example/cache/web/controllers/MovieControllerIT.java
index 538cd344b..5bed47b2c 100644
--- a/r2dbc/boot-reactive-cache/src/test/java/com/example/cache/web/controllers/MovieControllerIT.java
+++ b/r2dbc/boot-reactive-cache/src/test/java/com/example/cache/web/controllers/MovieControllerIT.java
@@ -43,13 +43,6 @@ void shouldFetchAllMovies() {
.isOk()
.expectBodyList(MovieResponse.class)
.hasSize(movieFlux.collectList().block().size());
- // .andExpect(jsonPath("$.totalElements", is(3)))
- // .andExpect(jsonPath("$.pageNumber", is(1)))
- // .andExpect(jsonPath("$.totalPages", is(1)))
- // .andExpect(jsonPath("$.isFirst", is(true)))
- // .andExpect(jsonPath("$.isLast", is(true)))
- // .andExpect(jsonPath("$.hasNext", is(false)))
- // .andExpect(jsonPath("$.hasPrevious", is(false)));
}
@Test
@@ -73,6 +66,24 @@ void shouldFindMovieById() {
.isEqualTo(movie.title());
}
+ @Test
+ void shouldReturn404WhenFetchingNonExistingMovie() {
+ this.webTestClient
+ .get()
+ .uri("/api/movies/{id}", 10000)
+ .accept(MediaType.APPLICATION_JSON)
+ .exchange()
+ .expectStatus()
+ .isNotFound()
+ .expectHeader()
+ .contentType(MediaType.APPLICATION_PROBLEM_JSON)
+ .expectBody()
+ .json(
+ """
+ {"type":"https://api.boot-reactive-cache.com/errors/not-found","title":"Not Found","status":404,"detail":"Movie with Id '10000' not found","instance":"/api/movies/10000","errorCategory":"Generic"}
+ """);
+ }
+
@Test
void shouldCreateNewMovie() {
MovieRequest movieRequest = new MovieRequest("New Movie");
@@ -87,6 +98,10 @@ void shouldCreateNewMovie() {
.isCreated()
.expectHeader()
.contentType(MediaType.APPLICATION_JSON)
+ .expectHeader()
+ .contentLength(29)
+ .expectHeader()
+ .exists("Location")
.expectBody(MovieResponse.class)
.value(movieResponse -> {
assertThat(movieResponse.id()).isNotNull();
@@ -95,7 +110,7 @@ void shouldCreateNewMovie() {
}
@Test
- void shouldReturn400WhenCreateNewMovieWithoutText() throws Exception {
+ void shouldReturn400WhenCreateNewMovieWithoutTitle() {
MovieRequest movieRequest = new MovieRequest(null);
this.webTestClient
@@ -118,7 +133,7 @@ void shouldReturn400WhenCreateNewMovieWithoutText() throws Exception {
}
@Test
- void shouldUpdateMovie() throws Exception {
+ void shouldUpdateMovie() {
Long movieId = movieFlux.blockLast().id();
MovieRequest movieRequest = new MovieRequest("Updated Movie");
@@ -157,6 +172,23 @@ void shouldDeleteMovie() {
.isEqualTo(movie.id())
.jsonPath("$.title")
.isEqualTo(movie.title());
- ;
+ }
+
+ @Test
+ void shouldReturn404WhenDeletingNonExistingMovie() {
+ this.webTestClient
+ .delete()
+ .uri("/api/movies/{id}", 10000)
+ .accept(MediaType.APPLICATION_JSON)
+ .exchange()
+ .expectStatus()
+ .isNotFound()
+ .expectHeader()
+ .contentType(MediaType.APPLICATION_PROBLEM_JSON)
+ .expectBody()
+ .json(
+ """
+ {"type":"https://api.boot-reactive-cache.com/errors/not-found","title":"Not Found","status":404,"detail":"Movie with Id '10000' not found","instance":"/api/movies/10000","errorCategory":"Generic"}
+ """);
}
}