Skip to content

Commit

Permalink
Merge pull request #5 from davidorellana98/develop
Browse files Browse the repository at this point in the history
Integration of unit tests with Mockito and documentation with Swagger.
  • Loading branch information
davidorellana98 authored Dec 28, 2022
2 parents e05b298 + 14e4539 commit 1953a53
Show file tree
Hide file tree
Showing 26 changed files with 587 additions and 13 deletions.
53 changes: 50 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,52 @@
<h1 align="center">Proyecto Integrador: Booking System Rest API con MongoDB</h1>
<h1 style="text-align:center;">Proyecto Integrador: Booking System Rest API</h1>

## Diagrama Microservicio User
El proyecto fue realizado siguiendo las buenas prácticas de programación, además contiene las siguientes implementaciones:

## Diagrama Microservicio Booking
* Microservicios User, Booking y Authentication
* Java versión 11
* Gestor de Dependencias Maven
* Spring Boot versión 2.7.5
* Persistencia de datos con MongoDB
* Jwt y Spring Security
* Test unitarios con JUnit y Mockito
* Documentación con Swagger

## Documentación con Swagger

Puedes visualizar la estructura de los controladores en la interfaz gráfica de [Swagger](http://localhost:8080/swagger-ui/), para consultar los endpoints del CRUD.

Para tener acceso total se debe crear un usuario, teniendo en cuenta la siguiente estructura en formato JSON, por ejemplo:

```xml
{
"name": "luis",
"lastName": "orellana",
"age": 24,
"identityCard": "123456789",
"email": "luis@mail.com",
"password": "12345"
}
```

Luego, en el microservicio **auth** se debe ingresar el email y password, creado con anterioridad para poder
generar el token de acceso, que tendrá un tiempo de validez de 30 minutos (se puede modificar el tiempo de expiración) para poder interactuar con todos los controladores.
```xml
{
"email":"luis@mail.com",
"password":"12345"
}
```

## Diagrama de clases

### Microservicio User

![user](https://raw.github.com/davidorellana98/booking-system-rest-api-spring-boot/main/src/main/resources/images/user.png "user")

### Microservicio Booking

![booking](https://raw.github.com/davidorellana98/booking-system-rest-api-spring-boot/main/src/main/resources/images/booking.png "booking")

### Microservicio Authenticación

![auth](https://raw.github.com/davidorellana98/booking-system-rest-api-spring-boot/main/src/main/resources/images/auth.png "auth")
19 changes: 19 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,25 @@
<artifactId>spring-boot-starter-security</artifactId>
</dependency>

<!-- Swagger 2 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>3.0.0</version>
</dependency>

<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>

<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>3.0.0</version>
</dependency>

<!-- Test -->
<dependency>
<groupId>org.springframework.boot</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import com.davidorellana.bookingsystemrestapi.auth.security.jwt.OperationJwt;
import com.davidorellana.bookingsystemrestapi.user.model.data.User;
import com.davidorellana.bookingsystemrestapi.user.service.UserService;
import io.swagger.v3.oas.annotations.Operation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
Expand All @@ -30,6 +31,7 @@ public AuthenticationController(OperationJwt operationJwt, UserService userServi
this.userService = userService;
}

@Operation(summary = "Jwt generation to obtain the Token")
@PostMapping
public ResponseEntity<TokenDto> generateJwt(@RequestBody LoginDto loginDto) {
User userFound = userService.findUserByEmail(loginDto.getEmail());
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.davidorellana.bookingsystemrestapi.auth.dto;

import java.io.Serializable;
import java.util.Objects;

public class LoginDto implements Serializable {

Expand Down Expand Up @@ -32,4 +33,25 @@ public String getPassword() {
public void setPassword(String password) {
this.password = password;
}

@Override
public String toString() {
return "LoginDto{" +
"email='" + email + '\'' +
", password='" + password + '\'' +
'}';
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
LoginDto loginDto = (LoginDto) o;
return Objects.equals(email, loginDto.email) && Objects.equals(password, loginDto.password);
}

@Override
public int hashCode() {
return Objects.hash(email, password);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import java.io.Serializable;
import java.util.Date;
import java.util.Objects;

public class TokenDto implements Serializable {

Expand Down Expand Up @@ -32,4 +33,25 @@ public Date getExpirationJwt() {
public void setExpirationJwt(Date expirationJwt) {
this.expirationJwt = expirationJwt;
}

@Override
public String toString() {
return "TokenDto{" +
"jwt='" + jwt + '\'' +
", expirationJwt=" + expirationJwt +
'}';
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
TokenDto tokenDto = (TokenDto) o;
return Objects.equals(jwt, tokenDto.jwt) && Objects.equals(expirationJwt, tokenDto.expirationJwt);
}

@Override
public int hashCode() {
return Objects.hash(jwt, expirationJwt);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ protected void configure(AuthenticationManagerBuilder auth) throws Exception {
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/v2/api-docs", "/configuration/ui", "/swagger-resources/**", "/configuration/security", "/swagger-ui.html", "/webjars/**", "/swagger-ui/**").permitAll()
.antMatchers(HttpMethod.POST,"/v1/users" ).permitAll()
.antMatchers(HttpMethod.POST, "/v1/auth").permitAll()
.anyRequest().authenticated()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public class OperationJwtImpl implements OperationJwt {
@Value("${KEY_SECRET}")
private String keySecret;

private final Integer MINUTES_JWT_EXPIRATION = 45;
private final Integer MINUTES_JWT_EXPIRATION = 30;

@Override
public String generateJwt(User user, Calendar expirationDate) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,12 @@
@Service
public class UserDetailServiceAuth implements UserDetailsService {

private final UserRepositoryDao userRepositoryDao;

@Autowired
private UserRepositoryDao userRepositoryDao;
public UserDetailServiceAuth(UserRepositoryDao userRepositoryDao) {
this.userRepositoryDao = userRepositoryDao;
}

@Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.davidorellana.bookingsystemrestapi.booking.model.data.Booking;
import com.davidorellana.bookingsystemrestapi.booking.model.dto.BookingDto;
import com.davidorellana.bookingsystemrestapi.booking.service.BookingService;
import io.swagger.v3.oas.annotations.Operation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
Expand All @@ -22,15 +23,17 @@ public BookingController(BookingService bookingService) {
this.bookingService = bookingService;
}

@Operation(summary = "Find all bookings")
@GetMapping
public ResponseEntity<List<Booking>> getAllBookings() {
public ResponseEntity<List<Booking>> findAllBookings() {
List<Booking> allBookings = bookingService.findAllBookings();
if (allBookings.isEmpty()) {
return new ResponseEntity("The bookings collection is empty.", HttpStatus.NOT_FOUND);
}
return new ResponseEntity<>(allBookings, HttpStatus.OK);
}

@Operation(summary = "Find a booking by its id")
@GetMapping("/{id}")
public ResponseEntity<Booking> findBookingById(@PathVariable String id) {
Booking bookingByIdFound = bookingService.findBookingById(id);
Expand All @@ -40,7 +43,8 @@ public ResponseEntity<Booking> findBookingById(@PathVariable String id) {
return new ResponseEntity("The id " + id + " does not exist in the bookings collection.", HttpStatus.NOT_FOUND);
}

@PostMapping()
@Operation(summary = "Create Booking")
@PostMapping
public ResponseEntity<Booking> createBooking(@RequestBody BookingDto bookingDto) {
Booking bookingCreated = bookingService.createBooking(bookingDto);
if (bookingCreated != null) {
Expand All @@ -50,6 +54,7 @@ public ResponseEntity<Booking> createBooking(@RequestBody BookingDto bookingDto)

}

@Operation(summary = "Update a booking by its id")
@PutMapping("/{id}")
public ResponseEntity<Booking> updateBookingById(@PathVariable String id, @RequestBody BookingDto bookingDto) {
Booking bookingUpdated = bookingService.updateBookingById(id, bookingDto);
Expand All @@ -59,6 +64,7 @@ public ResponseEntity<Booking> updateBookingById(@PathVariable String id, @Reque
return new ResponseEntity("Booking update failed due to inconsistent ID or dates.", HttpStatus.NOT_FOUND);
}

@Operation(summary = "Delete a booking by its id")
@DeleteMapping("/{id}")
public ResponseEntity<Boolean> deleteBookingById(@PathVariable String id) {
if (bookingService.deleteBookingById(id)) {
Expand All @@ -67,6 +73,7 @@ public ResponseEntity<Boolean> deleteBookingById(@PathVariable String id) {
return new ResponseEntity("The id " + id + " is not found in the collection of bookings to delete.", HttpStatus.NOT_FOUND);
}

@Operation(summary = "Delete all bookings")
@DeleteMapping
public ResponseEntity deleteAllBookings() {
List<Booking> allBookings = bookingService.findAllBookings();
Expand All @@ -77,7 +84,8 @@ public ResponseEntity deleteAllBookings() {
return new ResponseEntity("Correct deletion of the entire booking collection.", HttpStatus.OK);
}

@GetMapping("/bookingType/{bookingType}")
@Operation(summary = "Find bookings by its booking type")
@GetMapping("/bookingType/{bookingType}")
public ResponseEntity<List<Booking>> findBookingsByBookingType(@PathVariable String bookingType) {
List<Booking> bookingsByBookingTypeFound = bookingService.findBookingsByBookingType(bookingType);
if (bookingsByBookingTypeFound != null) {
Expand All @@ -86,6 +94,7 @@ public ResponseEntity<List<Booking>> findBookingsByBookingType(@PathVariable Str
return new ResponseEntity("The booking with the name " + bookingType + " is not found in the bookings collection.", HttpStatus.NOT_FOUND);
}

@Operation(summary = "Find bookings by its Payment Methods (CASH, CARD, PAYPAL, ALIPAY, BITCOIN)")
@GetMapping("/paymentMethods/{paymentMethods}")
public ResponseEntity<List<Booking>> findBookingsByPaymentMethods(@PathVariable String paymentMethods) {
List<Booking> bookingsByPaymentMethodsFound = bookingService.findBookingsByPaymentMethods(paymentMethods);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,17 @@ public Booking(BookingDto bookingDto) {
this.updateBookingCollection(bookingDto);
}

public Booking(String bookingType, Boolean reserved, LocalDate bookingStartDate, LocalDate bookingEndDate, PaymentMethods paymentMethods, Integer numberDaysBooking, Double priceBookingDay, Double totalPriceBooking) {
this.bookingType = bookingType;
this.reserved = reserved;
this.bookingStartDate = bookingStartDate;
this.bookingEndDate = bookingEndDate;
this.paymentMethods = paymentMethods;
this.numberDaysBooking = numberDaysBooking;
this.priceBookingDay = priceBookingDay;
this.totalPriceBooking = totalPriceBooking;
}

public String getId() {
return id;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,15 @@ public class BookingDto implements Serializable {

public BookingDto() { }

public BookingDto(String bookingType, Boolean reserved, LocalDate bookingStartDate, LocalDate bookingEndDate, PaymentMethods paymentMethods, Double priceBookingDay) {
this.bookingType = bookingType;
this.reserved = reserved;
this.bookingStartDate = bookingStartDate;
this.bookingEndDate = bookingEndDate;
this.paymentMethods = paymentMethods;
this.priceBookingDay = priceBookingDay;
}

public String getBookingType() {
return bookingType;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package com.davidorellana.bookingsystemrestapi.swagger;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpHeaders;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.*;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;

@Configuration
@EnableSwagger2
public class SwaggerConfiguration {

private ApiKey apiKey() {
return new ApiKey("JWT", HttpHeaders.AUTHORIZATION, "header");
}

private List<SecurityReference> defaultAuth() {
AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
authorizationScopes[0] = authorizationScope;
return Arrays.asList(new SecurityReference("JWT", authorizationScopes));
}

private SecurityContext securityContext() {
return SecurityContext.builder().securityReferences(defaultAuth()).build();
}

@Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.securityContexts(Arrays.asList(securityContext()))
.securitySchemes(Arrays.asList(apiKey()))
.select()
.apis(RequestHandlerSelectors.basePackage("com.davidorellana.bookingsystemrestapi"))
.paths(PathSelectors.any())
.build();
}

private ApiInfo apiInfo() {
return new ApiInfo(
"Documentation - Integrator Project: Booking System Rest Api",
"Booking System Rest Api Project: Implementation of User, Booking and Authentication microservices, with implementation to a NoSql MongoDB database, JWT and Security database, Unit Test.",
"v1.0.0",
"",
new Contact("Luis David Orellana", "https://www.linkedin.com/in/luisdavidorellana/", ""),
"Apache 2.0", "https://www.apache.org/licenses/LICENSE-2.0.html", Collections.emptyList());
}
}
Loading

0 comments on commit 1953a53

Please sign in to comment.