Skip to content
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
26 changes: 13 additions & 13 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,19 @@ on:
- "**"
jobs:
build:
name: Maven Build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3

- name: Setup Java 17
uses: actions/setup-java@v3
with:
java-version: 17
distribution: "temurin"

- name: Build with Maven
run: mvn verify --file skill-tree/pom.xml -Pgit-build-profile -Dskip-tests=true
name: Maven Build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3

- name: Setup Java 17
uses: actions/setup-java@v3
with:
java-version: 17
distribution: "temurin"

- name: Build with Maven
run: mvn verify --file skill-tree/pom.xml -P git-build-profile -Dskip-tests=true

unit-test:
name: Unit Tests
Expand Down
9 changes: 7 additions & 2 deletions skill-tree/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,11 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>

<!--Start io.jsonwebtoken: todo: remove the below block when rolling back to new version -->
<dependency>
Expand Down Expand Up @@ -214,7 +219,7 @@
<skip>${skip-ut}</skip>
<skip>${skip-tests}</skip>
<includes>
<include>**/unit/*.java</include>
<include>**/unit/**/*.java</include>
</includes>

</configuration>
Expand All @@ -226,7 +231,7 @@
<configuration>
<skip>${skip-tests}</skip>
<includes>
<include>**/integration/*.java</include>
<include>**/integration/**/*.java</include>
</includes>

</configuration>
Expand Down
10 changes: 6 additions & 4 deletions skill-tree/src/main/java/com/RDS/skilltree/apis/SkillsApi.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,14 @@ public ResponseEntity<List<SkillViewModel>> getAll() {

@GetMapping("/requests")
public ResponseEntity<SkillRequestsDto> getAllRequests(
@RequestParam(value = "status", required = false) UserSkillStatusEnum status) {
@RequestParam(name = "status", required = false) UserSkillStatusEnum status,
@RequestParam(name = "dev", required = false, defaultValue = "false") boolean isDev) {

if (status != null) {
return ResponseEntity.ok(skillService.getRequestsByStatus(status));
return ResponseEntity.ok(skillService.getRequestsByStatus(status, isDev));
}

return ResponseEntity.ok(skillService.getAllRequests());
return ResponseEntity.ok(skillService.getAllRequests(isDev));
}

@PostMapping("/requests/{skillId}/action")
Expand All @@ -57,7 +59,7 @@ public ResponseEntity<GenericResponse<String>> approveRejectSkillRequest(
@PostMapping
@AuthorizedRoles({UserRoleEnum.SUPERUSER})
public ResponseEntity<SkillViewModel> create(@Valid @RequestBody CreateSkillViewModel skill) {
return ResponseEntity.ok(skillService.create(skill));
return ResponseEntity.status(HttpStatus.CREATED).body(skillService.create(skill));
}

@GetMapping("/{id}/endorsements")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.RDS.skilltree.exceptions;

public class EndorsementAlreadyExistsException extends RuntimeException {
public EndorsementAlreadyExistsException(String message) {
super(message);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,7 @@

import com.RDS.skilltree.utils.GenericResponse;
import jakarta.validation.ConstraintViolationException;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import lombok.extern.slf4j.Slf4j;
import org.apache.tomcat.websocket.AuthenticationException;
import org.springframework.http.HttpStatus;
Expand All @@ -16,16 +13,15 @@
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.WebRequest;

@Slf4j
@ControllerAdvice
public class GlobalExceptionHandler {

@ExceptionHandler({NoEntityException.class})
public ResponseEntity<GenericResponse<Object>> handleNoEntityException(NoEntityException ex) {
log.error("NoEntityException - Error : {}", ex.getMessage(), ex);
return ResponseEntity.status(HttpStatus.NOT_FOUND)
.body(new GenericResponse<>(null, ex.getMessage()));
log.error("NoEntityException - Error : {}", ex.getMessage());
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(new GenericResponse<>(ex.getMessage()));
}

@ExceptionHandler({AuthenticationException.class, InsufficientAuthenticationException.class})
Expand All @@ -40,25 +36,21 @@ public ResponseEntity<GenericResponse<Object>> handleInvalidBearerTokenException
@ExceptionHandler({AccessDeniedException.class})
public ResponseEntity<GenericResponse<Object>> handleAccessDeniedException(
AccessDeniedException ex) {
return ResponseEntity.status(HttpStatus.FORBIDDEN)
.body(new GenericResponse<>(null, ex.getMessage()));
return ResponseEntity.status(HttpStatus.FORBIDDEN).body(new GenericResponse<>(ex.getMessage()));
}

@ExceptionHandler({EntityAlreadyExistsException.class})
public ResponseEntity<GenericResponse<Object>> handleEntityAlreadyExistsException(
EntityAlreadyExistsException ex) {
log.error("EntityAlreadyExistsException - Error : {}", ex.getMessage(), ex);
return ResponseEntity.status(HttpStatus.CONFLICT)
.body(new GenericResponse<>(null, ex.getMessage()));
log.error("EntityAlreadyExistsException - Error : {}", ex.getMessage());
return ResponseEntity.status(HttpStatus.CONFLICT).body(new GenericResponse<>(ex.getMessage()));
}

@ExceptionHandler({RuntimeException.class})
public ResponseEntity<GenericResponse<Object>> handleRuntimeException(RuntimeException ex) {
log.error("Runtime Exception - Error : {}", ex.getMessage(), ex);
log.error("Runtime Exception - Error : {}", ex.getMessage());
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(
new GenericResponse<>(
null, "Runtime Exception - Something went wrong, please try again."));
.body(new GenericResponse<>("Runtime Exception - Something went wrong, please try again."));
}

@ExceptionHandler({MethodArgumentNotValidException.class})
Expand All @@ -73,85 +65,81 @@ public ResponseEntity<GenericResponse<Object>> handleMethodArgumentNotValidExcep
}
log.error("MethodArgumentNotValidException Exception - Error : {}", ex.getMessage(), ex);
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
.body(new GenericResponse<>(null, errorString.toString().trim()));
.body(new GenericResponse<>(errorString.toString().trim()));
}

@ExceptionHandler({Exception.class})
public ResponseEntity<GenericResponse<Object>> handleException(Exception ex) {
log.error("Exception - Error : {}", ex.getMessage(), ex);
log.error("Exception - Error : {}", ex.getMessage());
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(new GenericResponse<>(null, "Something unexpected happened, please try again."));
.body(new GenericResponse<>("Something unexpected happened, please try again."));
}

@ExceptionHandler({InvalidParameterException.class})
public ResponseEntity<GenericResponse<Object>> handleException(InvalidParameterException ex) {
log.error("Exception - Error : {}", ex.getMessage(), ex);
log.error("InvalidParameterException - Error : {}", ex.getMessage());
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
.body(new GenericResponse<>(null, ex.getMessage()));
.body(new GenericResponse<>(ex.getMessage()));
}

@ExceptionHandler({ConstraintViolationException.class})
public ResponseEntity<GenericResponse<Object>> handleException(ConstraintViolationException ex) {
log.error("Exception - Error : {}", ex.getMessage(), ex);
log.error("ConstraintViolationException - Error : {}", ex.getMessage());
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
.body(new GenericResponse<>(null, ex.getMessage()));
.body(new GenericResponse<>(ex.getMessage()));
}

@ExceptionHandler(UserNotFoundException.class)
public ResponseEntity<?> handleUserNotFoundException(
UserNotFoundException ex, WebRequest request) {
log.error("Exception - Error : {}", ex.getMessage(), ex);
return new ResponseEntity<>(new GenericResponse<>(null, ex.getMessage()), HttpStatus.NOT_FOUND);
public ResponseEntity<?> handleUserNotFoundException(UserNotFoundException ex) {
log.error("UserNotFoundException - Error : {}", ex.getMessage());
return new ResponseEntity<>(new GenericResponse<>(ex.getMessage()), HttpStatus.NOT_FOUND);
}

@ExceptionHandler(SkillAlreadyExistsException.class)
public ResponseEntity<?> handleSkillAlreadyExistsException(
SkillAlreadyExistsException ex, WebRequest request) {
log.error("Exception - Error : {}", ex.getMessage(), ex);
return new ResponseEntity<>(new GenericResponse<>(null, ex.getMessage()), HttpStatus.CONFLICT);
public ResponseEntity<?> handleSkillAlreadyExistsException(SkillAlreadyExistsException ex) {
log.error("SkillAlreadyExistsException - Error : {}", ex.getMessage());
return new ResponseEntity<>(new GenericResponse<>(ex.getMessage()), HttpStatus.CONFLICT);
}

@ExceptionHandler(SelfEndorsementNotAllowedException.class)
public ResponseEntity<?> handleSelfEndorsementNotAllowedException(
SelfEndorsementNotAllowedException ex, WebRequest request) {
log.error("Exception - Error : {}", ex.getMessage(), ex);
SelfEndorsementNotAllowedException ex) {
log.error("SelfEndorsementNotAllowedException - Error : {}", ex.getMessage());
return new ResponseEntity<>(
new GenericResponse<>(null, ex.getMessage()), HttpStatus.METHOD_NOT_ALLOWED);
new GenericResponse<>(ex.getMessage()), HttpStatus.METHOD_NOT_ALLOWED);
}

@ExceptionHandler(SkillNotFoundException.class)
public ResponseEntity<?> handleSkillNotFoundException(
SkillNotFoundException ex, WebRequest request) {
log.error("Exception - Error : {}", ex.getMessage(), ex);
return new ResponseEntity<>(new GenericResponse<>(null, ex.getMessage()), HttpStatus.NOT_FOUND);
public ResponseEntity<?> handleSkillNotFoundException(SkillNotFoundException ex) {
log.error("SkillNotFoundException - Error : {}", ex.getMessage());
return new ResponseEntity<>(new GenericResponse<>(ex.getMessage()), HttpStatus.NOT_FOUND);
}

@ExceptionHandler(EndorsementNotFoundException.class)
public ResponseEntity<?> handleEndorsementNotException(
EndorsementNotFoundException ex, WebRequest request) {
log.error("Exception - Error : {}", ex.getMessage(), ex);
return new ResponseEntity<>(new GenericResponse<>(null, ex.getMessage()), HttpStatus.NOT_FOUND);
public ResponseEntity<?> handleEndorsementNotFoundException(EndorsementNotFoundException ex) {
log.error("EndorsementNotFoundException - Error : {}", ex.getMessage());
return new ResponseEntity<>(new GenericResponse<>(ex.getMessage()), HttpStatus.NOT_FOUND);
}

@ExceptionHandler(ForbiddenException.class)
public ResponseEntity<?> handleForbiddenException(ForbiddenException ex, WebRequest request) {
log.error("Exception - Error : {}", ex.getMessage(), ex);
return new ResponseEntity<>(new GenericResponse<>(null, ex.getMessage()), HttpStatus.FORBIDDEN);
public ResponseEntity<?> handleForbiddenException(ForbiddenException ex) {
log.error("ForbiddenException - Error : {}", ex.getMessage());
return new ResponseEntity<>(new GenericResponse<>(ex.getMessage()), HttpStatus.FORBIDDEN);
}

@ExceptionHandler(InternalServerErrorException.class)
public ResponseEntity<?> handleInternalServerErrorException(
InternalServerErrorException ex, WebRequest request) {
public ResponseEntity<?> handleInternalServerErrorException(InternalServerErrorException ex) {
log.error("Internal Server Error", ex);
// Create a more specific error message based on the exception type or cause
String errorMessage = "An unexpected error occurred.";
return new ResponseEntity<>(
new GenericResponse<>(errorMessage), HttpStatus.INTERNAL_SERVER_ERROR);
}

// Consider adding more details to the response for debugging
Map<String, Object> errorDetails = new HashMap<>();
errorDetails.put("timestamp", LocalDateTime.now());
errorDetails.put("message", errorMessage);
errorDetails.put("details", ex.getMessage()); // Include exception details for debugging

return new ResponseEntity<>(errorDetails, HttpStatus.INTERNAL_SERVER_ERROR);
@ExceptionHandler(EndorsementAlreadyExistsException.class)
public ResponseEntity<?> handleEndorsementAlreadyExistsException(
EndorsementAlreadyExistsException ex) {
log.error("EndorsementAlreadyExistsException - Error : {}", ex.getMessage());
return new ResponseEntity<>(
new GenericResponse<>(ex.getMessage()), HttpStatus.METHOD_NOT_ALLOWED);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,20 @@
import com.RDS.skilltree.models.Endorsement;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

public interface EndorsementRepository extends JpaRepository<Endorsement, Integer> {
List<Endorsement> findBySkillId(Integer skillId);

List<Endorsement> findByEndorseIdAndSkillId(String endorseId, Integer skillId);

@Query(
"SELECT (COUNT(*) > 0) AS exists "
+ "FROM Endorsement e "
+ "WHERE e.endorserId = :endorserId AND e.endorseId = :endorseId AND e.skill.id = :skillId")
boolean existsByEndorseIdAndEndorserIdAndSkillId(
@Param("endorseId") String endorseId,
@Param("endorserId") String endorserId,
@Param("skillId") Integer skillId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,21 @@ public interface UserSkillRepository extends JpaRepository<UserSkills, Integer>
List<UserSkills> findByUserIdAndSkillId(String userId, Integer skillId);

@Query(
"SELECT us FROM UserSkills us "
+ "JOIN Endorsement e ON us.userId = e.endorseId "
"SELECT us "
+ "FROM UserSkills us JOIN Endorsement e ON us.userId = e.endorseId "
+ "WHERE e.endorserId = :endorserId")
List<UserSkills> findUserSkillsByEndorserIdLegacy(@Param("endorserId") String endorserId);

@Query(
"SELECT us "
+ "FROM UserSkills us JOIN Endorsement e ON us.userId = e.endorseId AND us.skill.id = e.skill.id "
+ "WHERE e.endorserId = :endorserId")
List<UserSkills> findUserSkillsByEndorserId(@Param("endorserId") String endorserId);

@Query(
"SELECT us "
+ "FROM UserSkills us JOIN Endorsement e ON us.userId = e.endorseId AND us.skill.id = e.skill.id "
+ "WHERE e.endorserId = :endorserId AND us.status = :status")
List<UserSkills> findByStatusAndEndorserId(
@Param("status") UserSkillStatusEnum status, @Param("endorserId") String endorserId);
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.RDS.skilltree.services;

import com.RDS.skilltree.dtos.RdsGetUserDetailsResDto;
import com.RDS.skilltree.exceptions.EndorsementAlreadyExistsException;
import com.RDS.skilltree.exceptions.EndorsementNotFoundException;
import com.RDS.skilltree.exceptions.SelfEndorsementNotAllowedException;
import com.RDS.skilltree.exceptions.SkillNotFoundException;
Expand All @@ -12,6 +13,7 @@
import com.RDS.skilltree.repositories.SkillRepository;
import com.RDS.skilltree.repositories.UserSkillRepository;
import com.RDS.skilltree.services.external.RdsService;
import com.RDS.skilltree.utils.Constants.ExceptionMessages;
import com.RDS.skilltree.viewmodels.CreateEndorsementViewModel;
import com.RDS.skilltree.viewmodels.EndorsementViewModel;
import com.RDS.skilltree.viewmodels.UpdateEndorsementViewModel;
Expand Down Expand Up @@ -63,7 +65,6 @@ public List<EndorsementViewModel> getAllEndorsementsBySkillId(Integer skillId) {
}

@Override
// TODO : add a check for when a endorsement is already created by a user for a particular skill.
public EndorsementViewModel create(CreateEndorsementViewModel endorsementViewModel) {
String message = endorsementViewModel.getMessage();
Integer skillId = endorsementViewModel.getSkillId();
Expand All @@ -75,19 +76,28 @@ public EndorsementViewModel create(CreateEndorsementViewModel endorsementViewMod
String endorserId = jwtDetails.getRdsUserId();

if (Objects.equals(endorseId, endorserId)) {
log.info(
log.warn(
"Self endorsement not allowed, endorseId: {}, endorserId: {}", endorseId, endorserId);
throw new SelfEndorsementNotAllowedException("Self endorsement not allowed");
throw new SelfEndorsementNotAllowedException(ExceptionMessages.SELF_ENDORSEMENT_NOT_ALLOWED);
}

Optional<Skill> skillDetails = skillRepository.findById(skillId);

if (skillDetails.isEmpty()) {
log.info(String.format("Skill id: %s not found", skillId));
throw new SkillNotFoundException("Skill does not exist");
log.info("Skill id: {} not found", skillId);
throw new SkillNotFoundException(ExceptionMessages.SKILL_NOT_FOUND);
}

if (endorsementRepository.existsByEndorseIdAndEndorserIdAndSkillId(
endorseId, endorserId, skillId)) {
log.info(
"Endorsement already exists for endorseId: {}, endorserId: {}, skillId: {}",
endorseId,
endorserId,
skillId);
throw new EndorsementAlreadyExistsException(ExceptionMessages.ENDORSEMENT_ALREADY_EXISTS);
}

// Get endorse(person being endorsed) & endorser(person endorsing) details from RDS
RdsGetUserDetailsResDto endorseDetails = rdsService.getUserDetails(endorseId);
RdsGetUserDetailsResDto endorserDetails = rdsService.getUserDetails(endorserId);

Expand Down Expand Up @@ -125,7 +135,7 @@ public EndorsementViewModel update(Integer endorsementId, UpdateEndorsementViewM

if (exitingEndorsement.isEmpty()) {
log.info(String.format("Endorsement with id: %s not found", endorsementId));
throw new EndorsementNotFoundException("Endorsement not found");
throw new EndorsementNotFoundException(ExceptionMessages.ENDORSEMENT_NOT_FOUND);
}

Endorsement endorsement = exitingEndorsement.get();
Expand Down
Loading