Skip to content

Commit

Permalink
Merge branch 'main' into chore/local-checkstyle-linter
Browse files Browse the repository at this point in the history
  • Loading branch information
markkovari committed Jul 11, 2024
2 parents 574815a + b90193a commit a204c1b
Show file tree
Hide file tree
Showing 16 changed files with 259 additions and 81 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
* This is the main class of the application.
*/
@SpringBootApplication
public class BackendApplication {

public static void main(String[] args) {
SpringApplication.run(BackendApplication.class, args);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@
*/
@Controller
public class HealthCheckController {
@GetMapping("/health-check")
@GetMapping ("/health-check")
public ResponseEntity<String> healthCheck() {
return new ResponseEntity<>("OK", HttpStatus.OK);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,67 @@

import com.greenfoxacademy.backend.dtos.RegisterUserDto;
import com.greenfoxacademy.backend.services.UserService;
import java.net.URI;
import java.util.HashMap;
import java.util.Map;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.FieldError;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;


/**
* REST controller where endpoints are handled.
*/
@RestController
@RequiredArgsConstructor
@CrossOrigin(origins = "http://localhost:8080")
public class UserController {
private final UserService userService;

@CrossOrigin(origins = "http://localhost:5173")
@PostMapping("/register")
public ResponseEntity<?> registerUser(@RequestBody RegisterUserDto registerUserDto) {
if (!userService.emailValidation(registerUserDto.getEmail())) {
return ResponseEntity.badRequest().body("Email is not valid!");
} else if (userService.existsByEmail(registerUserDto.getEmail())) {
return ResponseEntity.badRequest().body("Email is already exist!");
} else {
userService.register(registerUserDto);
return ResponseEntity.ok().build();
/**
* This method registers a new user.
*
* @param registerUserDto the user to be registered
*
* @return a response entity with the status code and the location of the new user
*/
@CrossOrigin (origins = "http://localhost:5173")
@PostMapping ("/register")
public ResponseEntity<?> registerUser(@Validated @RequestBody RegisterUserDto registerUserDto) {
try {
URI uri = URI.create("/users/" + userService.register(registerUserDto).id());
return ResponseEntity.created(uri).build();
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getMessage());
}
}

/**
* This method handles validation exceptions.
*
* @param ex the exception to be handled
*
* @return a map with the field name and the error message
*/
@ResponseStatus (HttpStatus.BAD_REQUEST)
@ExceptionHandler (MethodArgumentNotValidException.class)
public Map<String, String> handleValidationExceptions(MethodArgumentNotValidException ex) {
Map<String, String> errors = new HashMap<>();
ex.getBindingResult().getAllErrors().forEach((error) -> {
String fieldName = ((FieldError) error).getField();
String errorMessage = error.getDefaultMessage();
errors.put(fieldName, errorMessage);
});
return errors;
}

}

Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.greenfoxacademy.backend.dtos;


/**
* RegisterResponseDto represents a registration response.
*
* @param id of the created user
*
*/

public record RegisterResponseDto(
Integer id
) {
public RegisterResponseDto {
}
}
Original file line number Diff line number Diff line change
@@ -1,18 +1,27 @@
package com.greenfoxacademy.backend.dtos;

import com.greenfoxacademy.backend.services.ValidPassword;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

/**
* Data Transfer Object for User Entity.
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Setter
public class RegisterUserDto {
@NotBlank
private String firstName;
@NotBlank
private String lastName;
@Email
private String email;
@ValidPassword
private String password;
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.greenfoxacademy.backend.models;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
Expand All @@ -11,7 +12,7 @@

/**
* Represents a user entity in the system. This class is a data model
* that maps to the user table in the database
* that maps to the user table in the database.
*/
@Data
@Builder
Expand All @@ -26,8 +27,8 @@ public class User {
private Integer id;
private String firstName;
private String lastName;
@Column(unique = true)
private String email;
private String password;


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package com.greenfoxacademy.backend.services;


import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
import java.util.ArrayList;


/**
* Custom validator for password field.
*/
public class PasswordConstraintValidator implements ConstraintValidator<ValidPassword, String> {

@Override
public boolean isValid(String password, ConstraintValidatorContext constraintValidatorContext) {
if (password == null) {
constraintValidatorContext
.disableDefaultConstraintViolation();
constraintValidatorContext
.buildConstraintViolationWithTemplate("Password should be added")
.addConstraintViolation();
return false;
}
boolean isValid = true;
ArrayList<String> errorMessages = new ArrayList<>();
if (password.length() < 8) {
errorMessages.add("must be at least 8 characters long");
isValid = false;
}
if (password.matches(".*\\s.*")) {
errorMessages.add("must not contain whitespace");
isValid = false;
}
if (!password.matches(".*\\d.*")) {
errorMessages.add("must contain at least one digit");
isValid = false;
}
if (!password.matches(".*[A-Z].*")) {
errorMessages.add("must contain at least one uppercase letter");
isValid = false;
}
if (!password.matches(".*[a-z].*")) {
errorMessages.add("must contain at least one lowercase letter");
isValid = false;
}
if (!isValid) {
constraintValidatorContext.disableDefaultConstraintViolation();
constraintValidatorContext
.buildConstraintViolationWithTemplate(
"Invalid password: " + String.join(", ", errorMessages)
)
.addConstraintViolation();
}
return isValid;
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.greenfoxacademy.backend.services;

import com.greenfoxacademy.backend.dtos.RegisterResponseDto;
import com.greenfoxacademy.backend.dtos.RegisterUserDto;
import com.greenfoxacademy.backend.models.User;
import org.springframework.stereotype.Service;
Expand All @@ -9,9 +10,5 @@
*/
@Service
public interface UserService {
void register(RegisterUserDto userDto);

Boolean emailValidation(String email);

boolean existsByEmail(String email);
RegisterResponseDto register(RegisterUserDto userDto) throws Exception;
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package com.greenfoxacademy.backend.services;

import com.greenfoxacademy.backend.dtos.RegisterResponseDto;
import com.greenfoxacademy.backend.dtos.RegisterUserDto;
import com.greenfoxacademy.backend.models.User;
import com.greenfoxacademy.backend.repositories.UserRepository;
import lombok.RequiredArgsConstructor;
import org.apache.commons.validator.routines.EmailValidator;
import org.modelmapper.ModelMapper;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
Expand All @@ -16,33 +16,21 @@
@RequiredArgsConstructor
public class UserServiceImpl implements UserService {
private final PasswordEncoder passwordEncoder;

private final UserRepository userRepository;
private final ModelMapper modelMapper;

@Override
public void register(RegisterUserDto userDto) {
User user = this.mapToEntity(userDto);
public RegisterResponseDto register(RegisterUserDto userDto) throws Exception {
User user = mapToEntity(userDto);
user.setPassword(passwordEncoder.encode(userDto.getPassword()));
userRepository.save(user);
return mapToRegisterResponseDto(userRepository.save(user));
}

private RegisterUserDto mapToDto(User user) {
RegisterUserDto registerUserDto = modelMapper.map(user, RegisterUserDto.class);
return registerUserDto;

private RegisterResponseDto mapToRegisterResponseDto(User user) {
return new RegisterResponseDto(user.getId());
}

private User mapToEntity(RegisterUserDto userDto) {
User user = modelMapper.map(userDto, User.class);
return user;
}

public Boolean emailValidation(String email) {
return EmailValidator.getInstance().isValid(email);
}

@Override
public boolean existsByEmail(String email) {
return userRepository.existsByEmail(email);
return modelMapper.map(userDto, User.class);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.greenfoxacademy.backend.services;


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

import jakarta.validation.Constraint;
import jakarta.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

/**
* ValidPassword annotation to mark a field as a password which should be validated.
*/
@Documented
@Constraint (validatedBy = PasswordConstraintValidator.class)
@Target ({TYPE, FIELD, ANNOTATION_TYPE})
@Retention(RUNTIME)
public @interface ValidPassword {

/**
* Message to be displayed when the password is invalid.
*/
String message() default "Invalid Password";

/**
* Groups.
*/
Class<?>[] groups() default {};

/**
* Payload.
*/
Class<? extends Payload>[] payload() default {};

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
package com.greenfoxacademy.backend.controller;

import static org.hamcrest.Matchers.is;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

import com.greenfoxacademy.backend.models.User;
import com.greenfoxacademy.backend.repositories.UserRepository;
import org.junit.jupiter.api.DisplayName;
Expand All @@ -12,10 +16,6 @@
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;

import static org.hamcrest.Matchers.is;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;


@SpringBootTest
@AutoConfigureMockMvc
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public class UserRegistrationTest {
private UserRepository userRepository;

@Test
public void userIsSuccessfulRegisteredInDatabase() {
public void userIsSuccessfulRegisteredInDatabase() throws Exception {
RegisterUserDto newUser = new RegisterUserDto();
newUser.setFirstName("John");
newUser.setLastName("Doe");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public class UserServiceTest {
private UserController userController;

@Test
public void registerMethodIsSuccessfullyCalled() {
public void registerMethodIsSuccessfullyCalled() throws Exception {
RegisterUserDto registerUserDto = new RegisterUserDto();
registerUserDto.setFirstName("John");
registerUserDto.setLastName("Doe");
Expand Down
2 changes: 1 addition & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,4 @@
"typescript": "^5.2.2",
"vite": "^5.2.0"
}
}
}
Loading

0 comments on commit a204c1b

Please sign in to comment.