Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(checkstyle-issues): Fix formatting issues after checkstyle failed #48

Merged
merged 2 commits into from
Aug 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,12 @@ booking. Below is a detailed overview of the key features included in the applic
you can explore the full API specification and interact with the API endpoints through the Swagger interface available
at [http://localhost:8080/swagger-ui/index.html](http://localhost:8080/swagger-ui/index.html).

Swagger is protected by basic auth and the credentials are provided by setting the env variables below:
```env
OPENAPI_USERNAME=
OPENAPI_PASSWORD=
```

### Auth

While not a requirement for the test, I have implemented a simple database authentication mechanism using the built-in
Expand Down
31 changes: 13 additions & 18 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -110,11 +110,6 @@
<artifactId>passay</artifactId>
<version>${passay.version}</version>
</dependency>
<dependency>
<groupId>com.nimbusds</groupId>
<artifactId>nimbus-jose-jwt</artifactId>
<version>${nimbus-jose-jwt.version}</version>
</dependency>

<!-- Code Linting -->
<dependency>
Expand Down Expand Up @@ -250,19 +245,19 @@
</plugin>

<!-- Maven Checkstyle Plugin -->
<!-- <plugin>-->
<!-- <groupId>org.apache.maven.plugins</groupId>-->
<!-- <artifactId>maven-checkstyle-plugin</artifactId>-->
<!-- <executions>-->
<!-- <execution>-->
<!-- <id>checkstyle</id>-->
<!-- <phase>validate</phase>-->
<!-- <goals>-->
<!-- <goal>check</goal>-->
<!-- </goals>-->
<!-- </execution>-->
<!-- </executions>-->
<!-- </plugin>-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<executions>
<execution>
<id>checkstyle</id>
<phase>validate</phase>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>

<!-- JaCoCo Maven Plugin -->
<plugin>
Expand Down
3 changes: 2 additions & 1 deletion src/main/java/com/mashreq/authentication/AuthController.java
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ public ResponseEntity<Void> signup(@Valid @RequestBody SignupPayload payload) {
}

/**
* Handle user login. Once user successfully authenticated generate JWT token and return with user details.
* Handle user login. Once user successfully authenticated generate JWT token
* and return with user details.
*
* @param loginPayload a login credentials
* @return a token with user details
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,8 @@ private Authentication authenticate(final String username, final String password
Objects.requireNonNull(password);

try {
return authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
return authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(username, password));
} catch (BadCredentialsException e) {
log.warn(e.getMessage(), e);
throw new AuthenticationException("Bad credentials", e);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
package com.mashreq.authentication.jpa;

import com.mashreq.security.BaseSecurityConfig;
import com.mashreq.security.SecurityUtils;
import com.mashreq.security.SimpleAccessDeniedHandler;
import com.mashreq.security.SimpleAuthenticationEntryPoint;
import com.mashreq.security.jwt.JwsService;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
Expand Down Expand Up @@ -57,7 +54,8 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf(AbstractHttpConfigurer::disable)
.cors(AbstractHttpConfigurer::disable)
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.sessionManagement(session ->
session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.exceptionHandling(this::configureExceptionHandling)
.authorizeHttpRequests(this::configureAuthorization)
.addFilterBefore(new JwtAuthenticationFilter(userDetailsService, jwsService),
Expand Down Expand Up @@ -97,13 +95,15 @@ public AuthenticationFailureHandler authenticationFailureHandler() {

public AuthenticationEntryPoint authenticationErrorHandler() {
return (request, response, authException) -> {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized: " + authException.getMessage());
response.sendError(
HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized: " + authException.getMessage());
};
}

public AccessDeniedHandler accessDeniedHandler() {
return (request, response, accessDeniedException) -> {
response.sendError(HttpServletResponse.SC_FORBIDDEN, "Forbidden: " + accessDeniedException.getMessage());
response.sendError(
HttpServletResponse.SC_FORBIDDEN, "Forbidden: " + accessDeniedException.getMessage());
};
}
}
15 changes: 10 additions & 5 deletions src/main/java/com/mashreq/bookings/BookingController.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,16 @@
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import jakarta.validation.Valid;
import java.util.UUID;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.*;

import java.util.UUID;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

/**
* Controller for managing bookings.
Expand Down Expand Up @@ -46,7 +49,8 @@ public BookingController(BookingService bookingService) {
consumes = MediaType.APPLICATION_JSON_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE
)
@Operation(summary = "Create a new booking", description = "Creates a new booking with the given details.")
@Operation(summary = "Create a new booking",
description = "Creates a new booking with the given details.")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "Successfully created the booking"),
@ApiResponse(responseCode = "400", description = "Invalid input provided")
Expand All @@ -67,7 +71,8 @@ public ResponseEntity<BookingResult> create(
* @return a response indicating the booking was successfully canceled
*/
@DeleteMapping(path = "/bookings/{id}")
@Operation(summary = "Cancel a booking", description = "Cancels an existing booking identified by the ID.")
@Operation(summary = "Cancel a booking",
description = "Cancels an existing booking identified by the ID.")
@ApiResponses(value = {
@ApiResponse(responseCode = "204", description = "Successfully canceled the booking"),
@ApiResponse(responseCode = "404", description = "Booking not found")
Expand Down
9 changes: 8 additions & 1 deletion src/main/java/com/mashreq/bookings/BookingRepository.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,14 @@
import org.springframework.data.jpa.repository.Query;

public interface BookingRepository extends JpaRepository<Booking, UUID> {
@Query("select b from Booking b where b.room.id in ?1 and DATE(b.startTime) = ?2 and b.status = 'BOOKED'")

@Query("""
select b
from Booking b
where b.room.id in ?1
and DATE(b.startTime) = ?2
and b.status = 'BOOKED'
""")
List<Booking> findByRoom_IdInAndToday(
Collection<UUID> ids, LocalDate today);
}
42 changes: 27 additions & 15 deletions src/main/java/com/mashreq/bookings/BookingService.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
import jakarta.persistence.OptimisticLockException;
import java.time.LocalDateTime;
import java.time.LocalTime;
import org.springframework.dao.OptimisticLockingFailureException;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
Expand Down Expand Up @@ -64,7 +63,8 @@ public BookingService(
* @param authenticatedUser the authenticated user making the booking
* @param payload the details of the booking request
* @return a result representing the outcome of the booking creation
* @throws NoRoomsAvailableException if no rooms are available for the requested time and capacity
* @throws NoRoomsAvailableException if no rooms are available for
* the requested time and capacity
*/
@Transactional
public BookingResult createBooking(AuthenticatedUser authenticatedUser, BookingRequest payload)
Expand All @@ -74,7 +74,8 @@ public BookingResult createBooking(AuthenticatedUser authenticatedUser, BookingR
while (retryCount < MAX_RETRIES) {
try {
User user = userService.getUserByUsername(authenticatedUser.getUsername());
Room room = roomService.getAvailableRoomWithOptimumCapacity(payload.startTime(), payload.endTime(), payload.numberOfPeople());
Room room = roomService.getAvailableRoomWithOptimumCapacity(
payload.startTime(), payload.endTime(), payload.numberOfPeople());

Booking booking = BookingRequest.toBooking(payload, user, room);
bookingRepository.save(booking);
Expand All @@ -85,15 +86,20 @@ public BookingResult createBooking(AuthenticatedUser authenticatedUser, BookingR
retryCount++;
if (retryCount >= MAX_RETRIES) {
log.error("Failed to create booking after " + MAX_RETRIES + " attempts", e);
throw new BookingFailedException(payload.startTime(), payload.endTime(), payload.numberOfPeople());
throw new BookingFailedException(
payload.startTime(), payload.endTime(), payload.numberOfPeople());
}
} catch (NoRoomsAvailableException e) {
log.warn("No available rooms found for the given time and capacity. Checking recurring bookings...");
checkRecurringMaintenanceBooking(payload.startTime(), payload.endTime(), payload.numberOfPeople());
log.warn(
"No available rooms found for the given time and capacity. Checking recurring bookings."
);
checkRecurringMaintenanceBooking(
payload.startTime(), payload.endTime(), payload.numberOfPeople());
throw e; // Re-throw to signal failure
}
}
throw new BookingFailedException(payload.startTime(), payload.endTime(), payload.numberOfPeople());
throw new BookingFailedException(
payload.startTime(), payload.endTime(), payload.numberOfPeople());
}

/**
Expand All @@ -102,7 +108,8 @@ public BookingResult createBooking(AuthenticatedUser authenticatedUser, BookingR
* @param startTime the start time of the booking request
* @param endTime the end time of the booking request
* @param numberOfPeople the number of people for the booking
* @throws MaintenanceInProgressException if there is an ongoing maintenance booking that overlaps with the request
* @throws MaintenanceInProgressException if there is an ongoing maintenance booking
* that overlaps with the request
*/
private void checkRecurringMaintenanceBooking(
LocalDateTime startTime, LocalDateTime endTime, int numberOfPeople)
Expand All @@ -113,9 +120,10 @@ private void checkRecurringMaintenanceBooking(
LocalTime startLocalTime = TimeUtils.convertLocalDateTimeToLocalTime(startTime);
LocalTime endLocalTime = TimeUtils.convertLocalDateTimeToLocalTime(endTime);

List<RecurringBooking> recurringBookings = recurringBookingRepository.findRecurringMaintenanceBooking(
startLocalTime, endLocalTime, numberOfPeople
);
List<RecurringBooking> recurringBookings =
recurringBookingRepository.findRecurringMaintenanceBooking(
startLocalTime, endLocalTime, numberOfPeople
);

if (!recurringBookings.isEmpty()) {
// If recurring maintenance found, throw an exception
Expand All @@ -129,7 +137,8 @@ private void checkRecurringMaintenanceBooking(
* @param authenticatedUser the user requesting the cancellation
* @param bookingId the ID of the booking to be cancelled
* @throws ResourceNotFoundException if the booking does not exist
* @throws AuthenticationException if the authenticated user is not authorized to cancel the booking
* @throws AuthenticationException if the authenticated user is not
* authorized to cancel the booking
*/
@Transactional
public void cancelBooking(AuthenticatedUser authenticatedUser, UUID bookingId) {
Expand All @@ -139,16 +148,19 @@ public void cancelBooking(AuthenticatedUser authenticatedUser, UUID bookingId) {
// Check if the booking exists
Booking booking = bookingOptional.orElseThrow(() -> {
log.error("Booking with ID {} not found.", bookingId);
return new ResourceNotFoundException(I18n.getMessage("error.booking.notFound", bookingId));
return new ResourceNotFoundException(
I18n.getMessage("error.booking.notFound", bookingId));
});

// Retrieve the user making the request
User user = userService.getUserByUsername(authenticatedUser.getUsername());

// Check if the authenticated user is the owner of the booking
if (!booking.getUser().equals(user)) {
log.warn("User {} is not authorized to cancel booking with ID {}.", user.getEmail(), bookingId);
throw new AuthenticationException(I18n.getMessage("error.booking.unauthorized", bookingId));
log.warn(
"User {} is not authorized to cancel booking with ID {}.", user.getEmail(), bookingId);
throw new AuthenticationException(
I18n.getMessage("error.booking.unauthorized", bookingId));
}

// Update the status of the booking
Expand Down
14 changes: 12 additions & 2 deletions src/main/java/com/mashreq/bookings/results/BookingResult.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.mashreq.bookings.results;

import com.mashreq.bookings.BookingType;
import com.mashreq.bookings.Booking;
import com.mashreq.bookings.BookingType;
import com.mashreq.rooms.Room;
import com.mashreq.users.User;
import java.time.LocalDateTime;
Expand All @@ -27,6 +27,16 @@ public static BookingResult toResult(Booking booking) {
int numberOfPeople = booking.getNumberOfPeople();

// Create and return a new BookingResult instance
return new BookingResult(booking.getId(), booking.getName(), booking.getDescription(), startTime, endTime, user, room, numberOfPeople, BookingType.MEETING);
return new BookingResult(
booking.getId(),
booking.getName(),
booking.getDescription(),
startTime,
endTime,
user,
room,
numberOfPeople,
BookingType.MEETING
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,15 @@ public static BookingSummaryResult toResult(Booking booking) {
int numberOfPeople = booking.getNumberOfPeople();

// Create and return a new BookingResult instance
return new BookingSummaryResult(booking.getName(), booking.getDescription(), startTime, endTime, user, numberOfPeople, BookingType.MEETING, room.getId());
return new BookingSummaryResult(
booking.getName(),
booking.getDescription(),
startTime,
endTime,
user,
numberOfPeople,
BookingType.MEETING,
room.getId());
}

public static BookingSummaryResult toResult(RecurringBooking booking) {
Expand All @@ -39,6 +47,15 @@ public static BookingSummaryResult toResult(RecurringBooking booking) {
Room room = booking.getRoom();

// Create and return a new BookingResult instance
return new BookingSummaryResult(booking.getName(), booking.getDescription(), startTime, endTime, user, 0, booking.getBookingType(), room.getId());
return new BookingSummaryResult(
booking.getName(),
booking.getDescription(),
startTime,
endTime,
user,
0,
booking.getBookingType(),
room.getId()
);
}
}
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
package com.mashreq.bookings.validators;

import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import jakarta.validation.Constraint;
import jakarta.validation.Payload;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

@Documented
@Constraint(validatedBy = IsMultipleOf15MinutesValidator.class)
@Target({ElementType.PARAMETER, FIELD })
@Target({ElementType.PARAMETER, FIELD})
@Retention(RUNTIME)
public @interface IsMultipleOf15Minutes {
String message() default "{error.time.15mins}";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,20 @@

import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;

public class IsMultipleOf15MinutesValidator implements ConstraintValidator<IsMultipleOf15Minutes, LocalDateTime> {
public class IsMultipleOf15MinutesValidator implements
ConstraintValidator<IsMultipleOf15Minutes, LocalDateTime> {

@Override
public boolean isValid(LocalDateTime LocalDateTime, ConstraintValidatorContext context) {
if (LocalDateTime == null) {
public boolean isValid(LocalDateTime localDateTime, ConstraintValidatorContext context) {
if (localDateTime == null) {
return true; // consider using @NotNull for mandatory validation
}

ZonedDateTime zonedDateTime = LocalDateTime.atZone(ZoneId.systemDefault());
ZonedDateTime zonedDateTime = localDateTime.atZone(ZoneId.systemDefault());
int minutes = zonedDateTime.getMinute();

return minutes % 15 == 0;
Expand Down
Loading
Loading