Skip to content

Commit

Permalink
feat: remove web flux and reactor (#39)
Browse files Browse the repository at this point in the history
* feat: remove spring web flux and add spring web mvc

* feat: user repository unit tests working

* ci: autogenerated JaCoCo coverage badge

* doc: update readme to remove web flux

* feat: better tests and docs

* ci: autogenerated JaCoCo coverage badge

* test: remove unused testing code blocks

* feat: improve documentation related to Mono

Co-authored-by: Ci Bot <cibot@users.noreply.github.com>
  • Loading branch information
2 people authored and DerekRoberts committed May 4, 2023
1 parent 9b0acf1 commit 67ac98c
Show file tree
Hide file tree
Showing 12 changed files with 334 additions and 517 deletions.
2 changes: 1 addition & 1 deletion .github/badges/branches.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion .github/badges/jacoco.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
19 changes: 11 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -275,15 +275,20 @@ and deploy it.
- Java ecosystem
- Maven
- Open JDK 17
- Spring WebFlux Framework with Reactor (reactive programming)
- Spring Web MVC Framework
- JPA and Hibernate Framework
- JUnit 5, Mockito
- Testing
- JUnit 5
- Mockito and Mock MVC
- Automated tests with Postman and Newman
- Database
- Remote Oracle with secure connection
- PostgreSQL
- DevOps
- Docker
- Sonar pipes
- Deploy to OpenShift cluster
- Docker Composer
- Sonar Cloud
- Deploy to OpenShift with GitHub Actions
- Tools (Recommendations)
- IntelliJ IDEA
- Postman
Expand All @@ -304,10 +309,8 @@ access configuration:
Then head to http://localhost:8090/actuator/health to check if the system was successfully launched:
the `status` property should have the value *UP*.

To run tests all you need is `./mvnw test`.

Before writing your first line of code, please take a moment and check out
our [CONTRIBUTING](CONTRIBUTING.md) guide.
Before writing your first line of code, and learn more about the checks, including
tests, please take a moment and check out our [CONTRIBUTING](CONTRIBUTING.md) guide.

## Getting help

Expand Down
19 changes: 1 addition & 18 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@
<!-- DevOps -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!-- Database -->
Expand All @@ -115,23 +115,6 @@
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
</dependency>

<!-- Documentation -->
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.0.2</version>
</dependency>

<dependency>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-annotations</artifactId>
<version>2.2.8</version>
</dependency>
</dependencies>

<build>
Expand Down
10 changes: 4 additions & 6 deletions src/main/java/ca/bc/gov/backendstartapi/config/CorsConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,13 @@
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.config.CorsRegistry;
import org.springframework.web.reactive.config.EnableWebFlux;
import org.springframework.web.reactive.config.WebFluxConfigurer;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/** This class holds the configuration for CORS handling. */
@Configuration
@EnableWebFlux
@Slf4j
public class CorsConfig implements WebFluxConfigurer {
public class CorsConfig implements WebMvcConfigurer {

@Value("${server.allowed.cors.origins}")
private String[] allowedOrigins;
Expand All @@ -33,6 +31,6 @@ public void addCorsMappings(CorsRegistry registry) {
.allowedOriginPatterns(allowedOrigins)
.allowedMethods("GET", "PUT", "POST", "DELETE", "PATCH", "OPTIONS", "HEAD");
}
WebFluxConfigurer.super.addCorsMappings(registry);
WebMvcConfigurer.super.addCorsMappings(registry);
}
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
package ca.bc.gov.backendstartapi.endpoint;

import ca.bc.gov.backendstartapi.response.BaseResponse;
import ca.bc.gov.backendstartapi.vo.CheckVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;

/** This class represents a check endpoint object. */
@Slf4j
Expand All @@ -25,9 +21,8 @@ public class CheckEndpoint {
* @return a CheckVo object containing a message and a release version
*/
@GetMapping(value = "/check", produces = MediaType.APPLICATION_JSON_VALUE)
public Mono<ResponseEntity<? extends BaseResponse>> check() {
public CheckVo check() {
log.info("nrbestapiVersion: {}", nrbestapiVersion);
CheckVo check = CheckVo.builder().message("OK").release(nrbestapiVersion).build();
return Mono.just(ResponseEntity.status(HttpStatus.OK).body(check));
return CheckVo.builder().message("OK").release(nrbestapiVersion).build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.bind.support.WebExchangeBindException;

/** This class is responsible for handling all kind of exceptions and validations. */
@RestControllerAdvice
Expand All @@ -20,11 +20,11 @@ public class RestExceptionEndpoint {
/**
* Handle all javax.validation exceptions.
*
* @param ex WebExchangeBindException instance
* @param ex MethodArgumentNotValidException instance
* @return a Map of String containing all the invalid fields and messages
*/
@ExceptionHandler(WebExchangeBindException.class)
ResponseEntity<ExceptionResponse> generalException(WebExchangeBindException ex) {
@ExceptionHandler(MethodArgumentNotValidException.class)
ResponseEntity<ExceptionResponse> generalException(MethodArgumentNotValidException ex) {
ExceptionResponse exResponse = new ExceptionResponse(ex.getFieldErrors().size());
List<FieldExceptionResponse> fieldList = new ArrayList<>();
for (FieldError fe : ex.getFieldErrors()) {
Expand Down
62 changes: 33 additions & 29 deletions src/main/java/ca/bc/gov/backendstartapi/endpoint/UserEndpoint.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package ca.bc.gov.backendstartapi.endpoint;

import ca.bc.gov.backendstartapi.dto.UserDto;
import ca.bc.gov.backendstartapi.exception.UserExistsException;
import ca.bc.gov.backendstartapi.exception.UserNotFoundException;
import ca.bc.gov.backendstartapi.repository.UserRepository;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import javax.validation.Valid;
import lombok.NoArgsConstructor;
import lombok.Setter;
Expand All @@ -16,8 +18,6 @@
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

/** This class exposes user related endpoints. */
@NoArgsConstructor
Expand All @@ -37,67 +37,74 @@ public UserEndpoint(UserRepository userRepository) {
* Create a user with first and last name.
*
* @param user a UserDto containing both first and last name
* @return a Mono instance containing the new user info
* @return a UserDto instance containing the new user info
*/
@PostMapping(
consumes = MediaType.APPLICATION_JSON_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE)
public Mono<UserDto> create(@Valid @RequestBody UserDto user) {
return userRepository.save(user).onErrorResume(UserExistsException.class, Mono::error);
public UserDto create(@Valid @RequestBody UserDto user) {
return userRepository.save(user);
}

/**
* Get a list of users given the last name.
*
* @param firstName user's first name
* @return A Flux instance containing all found users.
* @return A List containing all found users.
*/
@GetMapping(
value = "/find-all-by-first-name/{firstName}",
produces = MediaType.APPLICATION_JSON_VALUE)
public Flux<UserDto> readByFirstName(@PathVariable("firstName") String firstName) {
return userRepository
.findAllByFirstName(firstName)
.switchIfEmpty(Mono.error(new UserNotFoundException()));
public List<UserDto> readByFirstName(@PathVariable("firstName") String firstName) {
List<UserDto> userList = userRepository.findAllByFirstName(firstName);
if (userList.isEmpty()) {
throw new UserNotFoundException();
}
return userList;
}

/**
* Get a list of users given the last name.
*
* @param lastName user's last name
* @return A Flux instance containing all found users.
* @return A List containing all found users.
*/
@GetMapping(
value = "/find-all-by-last-name/{lastName}",
produces = MediaType.APPLICATION_JSON_VALUE)
public Flux<UserDto> readByLastName(@PathVariable("lastName") String lastName) {
return userRepository
.findByLastName(lastName)
.switchIfEmpty(Mono.error(new UserNotFoundException()));
public List<UserDto> readByLastName(@PathVariable("lastName") String lastName) {
List<UserDto> userList = userRepository.findAllByLastName(lastName);
if (userList.isEmpty()) {
throw new UserNotFoundException();
}
return userList;
}

/**
* Get a user given his first and last name.
*
* @param firstName user's first name
* @param lastName user's last name
* @return a Mono instance containing the found user or a 404 if not found.
* @return a UserDto instance containing the found user or a 404 if not found.
*/
@GetMapping(value = "/find/{firstName}/{lastName}", produces = MediaType.APPLICATION_JSON_VALUE)
public Mono<UserDto> readByUser(
public UserDto readByUser(
@PathVariable("firstName") String firstName, @PathVariable("lastName") String lastName) {
return userRepository
.find(firstName, lastName)
.switchIfEmpty(Mono.error(new UserNotFoundException()));
Optional<UserDto> userDtoOp = userRepository.find(firstName, lastName);
if (userDtoOp.isEmpty()) {
throw new UserNotFoundException();
}

return userDtoOp.get();
}

/**
* Get a list with all registered users.
*
* @return a Mono instance containing the found user or a 404 if not found.
* @return a Collection containing all found users or a 404 if not found.
*/
@GetMapping(value = "/find-all", produces = MediaType.APPLICATION_JSON_VALUE)
public Flux<UserDto> readAllUsers() {
public Collection<UserDto> readAllUsers() {
return userRepository.findAll();
}

Expand All @@ -106,14 +113,11 @@ public Flux<UserDto> readAllUsers() {
*
* @param firstName user's first name
* @param lastName user's last name
* @return a Mono instance containing the removed user info
* @return a UserDto instance containing the removed user info.
*/
@DeleteMapping(value = "/{firstName}/{lastName}", produces = MediaType.APPLICATION_JSON_VALUE)
public Mono<UserDto> deleteUser(
public UserDto deleteUser(
@PathVariable("firstName") String firstName, @PathVariable("lastName") String lastName) {
return userRepository
.find(firstName, lastName)
.flatMap(user -> userRepository.delete(user))
.switchIfEmpty(Mono.error(new UserNotFoundException()));
return userRepository.delete(new UserDto(firstName, lastName));
}
}
Loading

0 comments on commit 67ac98c

Please sign in to comment.