Skip to content

Commit

Permalink
build a skills/requests api & create a rds service to get user deta…
Browse files Browse the repository at this point in the history
…ils (#139)

* create a model to store user_skill and add remove unused columns in users and skills model

* create a model to store user_skill and add/remove unused columns in users and skills model

* change id to integer

* check if a skill already exists before creating one, make `updated_at` optional in TrackedProperties

* remove unit and integration test and remove skill service & skill service implementation

* add todo

* rename SkillType to SkillTypeEnum

* set logging level debug in application-dev instead of application

* add todo

* remove endorsements list

* add reference to user table in skills modal

* Build api to create a new endorsement

* create api to update an endorsement

* remove unused files

* remove unused code in endorsement controller

* chagne import order

* change the skills project structure to match the new one

* rename exceptions folder to small case exceptions and create user not found and skill already exists exceptions

* create enums folder and move skill type enum to the folder

* rename Conifg to config, move generic response and jwtAuthenticationFilter to utils

* add api to get all endorsements for a skill using skill id in skillsapi

* remove skills package

* fix build error

* move api to create a endorsement to the new folder structure

* add api to update endorsement in apis/endorsements api and remove old Endorsement folder

* move health check api to the api folder & metric service inside the services folder

* fix formatting

* create annotation and aspect to handle authorized roles to an api

* add authorizred roles annotation to skillsapi & endorsementsapi class

* set authorized role for creating a skill to only superuser

* fix formatting

* create api to get all skill requests

* apply spotless

* set endorsement id

* run spotlesscheck

* remove comments

* add super user role check in skills/requests api

* build the `skill/requests` api to get all skill requests and their endorsements

* fix duplicate data issue when getting all skill requests

* improve comment

* change data to requests in SkillRequestDto

* fix formatting

* fix wrong id being set in endorserDetails

* fix formatting error

* change yoe to float from int in RdsUserViewModel

* add try catch block to getUserDetails

* add try catch and create a constructor in SkillRequestViewModel
  • Loading branch information
yesyash authored Jul 26, 2024
1 parent 424813a commit ba3cf55
Show file tree
Hide file tree
Showing 18 changed files with 329 additions and 28 deletions.
1 change: 1 addition & 0 deletions .env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ MYSQL_DB_USERNAME=testuser
MYSQL_DB_PASSWORD=testpassword
MYSQL_ROOT_PASSWORD=password
DB_DDL_POLICY=update
RDS_BACKEND_BASE_URL=http://localhost:3000
RDS_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAg5HrGgKFmH485DXLG4fG
1mKNXceWthXgAozsUGHxLIM3Fa5wU+tLi7tLDZ6LRKo4ZZV2gJJdDqFSNsvn1Uvr
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
public class UserSkillsModel {
@Id
@GeneratedValue
@Column(name = "id", columnDefinition = "BINARY(16)")
@Column(name = "id")
private UUID id;

@JsonBackReference
Expand Down
17 changes: 9 additions & 8 deletions skill-tree/src/main/java/com/RDS/skilltree/apis/SkillsApi.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,16 @@

import com.RDS.skilltree.User.UserRoleEnum;
import com.RDS.skilltree.annotations.AuthorizedRoles;
import com.RDS.skilltree.dtos.SkillRequestsDto;
import com.RDS.skilltree.services.EndorsementService;
import com.RDS.skilltree.services.SkillService;
import com.RDS.skilltree.viewmodels.CreateSkillViewModel;
import com.RDS.skilltree.viewmodels.EndorsementViewModel;
import com.RDS.skilltree.viewmodels.SkillViewModel;
import jakarta.validation.Valid;
import jakarta.validation.constraints.Min;
import java.util.List;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

Expand All @@ -31,18 +29,21 @@ public ResponseEntity<List<SkillViewModel>> getAll() {
return ResponseEntity.ok(skillService.getAll());
}

@GetMapping("/requests")
@AuthorizedRoles({UserRoleEnum.SUPERUSER})
public ResponseEntity<SkillRequestsDto> getAllRequests() {
return ResponseEntity.ok(skillService.getAllRequests());
}

@PostMapping
@AuthorizedRoles({UserRoleEnum.SUPERUSER})
public ResponseEntity<SkillViewModel> create(@Valid @RequestBody CreateSkillViewModel skill) {
return ResponseEntity.ok(skillService.create(skill));
}

@GetMapping("/{id}/endorsements")
public ResponseEntity<Page<EndorsementViewModel>> getEndorsementsBySkillId(
@RequestParam(name = "offset", defaultValue = "0", required = false) @Min(0) int offset,
@RequestParam(name = "limit", defaultValue = "10", required = false) @Min(1) int limit,
public ResponseEntity<List<EndorsementViewModel>> getEndorsementsBySkillId(
@PathVariable(value = "id") Integer skillID) {
PageRequest pageRequest = PageRequest.of(offset, limit);
return ResponseEntity.ok(endorsementService.getAllEndorsementsBySkillId(skillID, pageRequest));
return ResponseEntity.ok(endorsementService.getAllEndorsementsBySkillId(skillID));
}
}
13 changes: 13 additions & 0 deletions skill-tree/src/main/java/com/RDS/skilltree/config/AppConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.RDS.skilltree.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@Configuration
public class AppConfig {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.RDS.skilltree.dtos;

import com.RDS.skilltree.viewmodels.SkillRequestViewModel;
import com.RDS.skilltree.viewmodels.UserViewModel;
import java.util.List;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
public class SkillRequestsDto {
private List<SkillRequestViewModel> requests;
private List<UserViewModel> users;

public static SkillRequestsDto toDto(
List<SkillRequestViewModel> skillRequests, List<UserViewModel> users) {
SkillRequestsDto skillRequestsDto = new SkillRequestsDto();
skillRequestsDto.setRequests(skillRequests);
skillRequestsDto.setUsers(users);

return skillRequestsDto;
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
package com.RDS.skilltree.repositories;

import com.RDS.skilltree.models.Endorsement;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;

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

List<Endorsement> findByEndorseIdAndSkillId(String endorseId, Integer skillId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.RDS.skilltree.repositories;

import com.RDS.skilltree.User.UserSkillStatusEnum;
import com.RDS.skilltree.User.UserSkillsModel;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;

public interface UserSkillRepository extends JpaRepository<UserSkillsModel, Integer> {
List<UserSkillsModel> findByStatus(UserSkillStatusEnum status);

List<UserSkillsModel> findByUserIdAndSkillId(String userId, Integer skillId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,10 @@
import com.RDS.skilltree.viewmodels.CreateEndorsementViewModel;
import com.RDS.skilltree.viewmodels.EndorsementViewModel;
import com.RDS.skilltree.viewmodels.UpdateEndorsementViewModel;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import java.util.List;

public interface EndorsementService {
Page<EndorsementViewModel> getAllEndorsementsBySkillId(Integer skillId, Pageable pageable);
List<EndorsementViewModel> getAllEndorsementsBySkillId(Integer skillId);

EndorsementViewModel create(CreateEndorsementViewModel endorsement);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import com.RDS.skilltree.User.UserModel;
import com.RDS.skilltree.User.UserRepository;
import com.RDS.skilltree.User.UserSkillStatusEnum;
import com.RDS.skilltree.User.UserSkillsModel;
import com.RDS.skilltree.exceptions.EndorsementNotFoundException;
import com.RDS.skilltree.exceptions.SelfEndorsementNotAllowedException;
import com.RDS.skilltree.exceptions.SkillNotFoundException;
Expand All @@ -10,14 +12,14 @@
import com.RDS.skilltree.models.Skill;
import com.RDS.skilltree.repositories.EndorsementRepository;
import com.RDS.skilltree.repositories.SkillRepository;
import com.RDS.skilltree.repositories.UserSkillRepository;
import com.RDS.skilltree.viewmodels.CreateEndorsementViewModel;
import com.RDS.skilltree.viewmodels.EndorsementViewModel;
import com.RDS.skilltree.viewmodels.UpdateEndorsementViewModel;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;

@Service
Expand All @@ -26,12 +28,12 @@ public class EndorsementServiceImplementation implements EndorsementService {
private final UserRepository userRepository;
private final SkillRepository skillRepository;
private final EndorsementRepository endorsementRepository;
private final UserSkillRepository userSkillRepository;

@Override
public Page<EndorsementViewModel> getAllEndorsementsBySkillId(
Integer skillId, Pageable pageable) {
Page<Endorsement> endorsementPage = endorsementRepository.findBySkillId(skillId, pageable);
return endorsementPage.map(EndorsementViewModel::toViewModel);
public List<EndorsementViewModel> getAllEndorsementsBySkillId(Integer skillId) {
List<Endorsement> endorsements = endorsementRepository.findBySkillId(skillId);
return endorsements.stream().map(EndorsementViewModel::toViewModel).toList();
}

@Override
Expand All @@ -42,7 +44,7 @@ public EndorsementViewModel create(CreateEndorsementViewModel endorsementViewMod
String endorseId = endorsementViewModel.getEndorseId();

// TODO: Get this from security context once the login api is implemented.
String endorserId = "ae7a6673c5574140838f209de4c644fc";
String endorserId = "cf8893a16cee42cc94387a9bd086ed46";

if (Objects.equals(endorseId, endorserId)) {
throw new SelfEndorsementNotAllowedException("Self endorsement not allowed");
Expand All @@ -64,14 +66,27 @@ public EndorsementViewModel create(CreateEndorsementViewModel endorsementViewMod
throw new SkillNotFoundException(String.format("Skill id: %s not found", skillId));
}

List<UserSkillsModel> userSkillEntry =
userSkillRepository.findByUserIdAndSkillId(endorseId, skillId);
Endorsement endorsement = new Endorsement();

endorsement.setMessage(message);
endorsement.setSkill(skillDetails.get());
endorsement.setEndorse(endorseDetails.get());
endorsement.setEndorser(endorserDetails.get());

return EndorsementViewModel.toViewModel(endorsementRepository.save(endorsement));
if (userSkillEntry.isEmpty()) {
UserSkillsModel userSkillsModel = new UserSkillsModel();
userSkillsModel.setUser(endorseDetails.get());
userSkillsModel.setSkill(skillDetails.get());
userSkillsModel.setStatus(UserSkillStatusEnum.PENDING);

userSkillRepository.save(userSkillsModel);
}

Endorsement newEndorsement = endorsementRepository.save(endorsement);

return EndorsementViewModel.toViewModel(newEndorsement);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.RDS.skilltree.services;

import com.RDS.skilltree.dtos.SkillRequestsDto;
import com.RDS.skilltree.viewmodels.CreateSkillViewModel;
import com.RDS.skilltree.viewmodels.SkillViewModel;
import java.util.List;
Expand All @@ -8,4 +9,6 @@ public interface SkillService {
List<SkillViewModel> getAll();

SkillViewModel create(CreateSkillViewModel skill);

SkillRequestsDto getAllRequests();
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
package com.RDS.skilltree.services;

import com.RDS.skilltree.User.JwtUserModel;
import com.RDS.skilltree.User.UserModel;
import com.RDS.skilltree.User.UserRepository;
import com.RDS.skilltree.User.*;
import com.RDS.skilltree.dtos.SkillRequestsDto;
import com.RDS.skilltree.exceptions.SkillAlreadyExistsException;
import com.RDS.skilltree.exceptions.UserNotFoundException;
import com.RDS.skilltree.models.Endorsement;
import com.RDS.skilltree.models.Skill;
import com.RDS.skilltree.repositories.EndorsementRepository;
import com.RDS.skilltree.repositories.SkillRepository;
import com.RDS.skilltree.viewmodels.CreateSkillViewModel;
import com.RDS.skilltree.viewmodels.SkillViewModel;
import com.RDS.skilltree.repositories.UserSkillRepository;
import com.RDS.skilltree.services.external.RdsService;
import com.RDS.skilltree.viewmodels.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
Expand All @@ -20,8 +24,11 @@
@Service
@RequiredArgsConstructor
public class SkillServiceImplementation implements SkillService {
private final RdsService rdsService;
private final SkillRepository skillRepository;
private final UserRepository userRepository;
private final UserSkillRepository userSkillRepository;
private final EndorsementRepository endorsementRepository;

@Override
public List<SkillViewModel> getAll() {
Expand All @@ -30,6 +37,71 @@ public List<SkillViewModel> getAll() {
.collect(Collectors.toList());
}

@Override
public SkillRequestsDto getAllRequests() {
List<UserSkillsModel> pendingSkills =
userSkillRepository.findByStatus(UserSkillStatusEnum.PENDING);

// store all users data that are a part of this request
Map<String, UserViewModel> userDetails = new HashMap<>();

// make a list of all pending skill requests with their endorsement details
List<SkillRequestViewModel> skillRequests =
pendingSkills.stream()
.map(
skill -> {
Integer skillId = skill.getSkill().getId();
String endorseId = skill.getUser().getId();
String endorseRdsUserId = skill.getUser().getRdsUserId();

// Get all endorsement for a specific skill and user Id
List<Endorsement> endorsements =
endorsementRepository.findByEndorseIdAndSkillId(endorseId, skillId);

if (!userDetails.containsKey(endorseId)) {
RdsUserViewModel endorseRdsDetails =
rdsService.getUserDetails(endorseRdsUserId);
UserViewModel endorseDetails =
getUserModalFromRdsDetails(endorseId, endorseRdsDetails);
userDetails.put(endorseId, endorseDetails);
}

endorsements.forEach(
endorsement -> {
String endorserId = endorsement.getEndorser().getId();
String endorserRdsUserId = endorsement.getEndorser().getRdsUserId();

if (!userDetails.containsKey(endorserId)) {
RdsUserViewModel endorserRdsDetails =
rdsService.getUserDetails(endorserRdsUserId);
UserViewModel endorserDetails =
getUserModalFromRdsDetails(endorserId, endorserRdsDetails);
userDetails.put(endorserId, endorserDetails);
}
});

return SkillRequestViewModel.toViewModel(skill, endorsements);
})
.toList();

return SkillRequestsDto.toDto(skillRequests, userDetails.values().stream().toList());
}

private static UserViewModel getUserModalFromRdsDetails(String id, RdsUserViewModel rdsDetails) {
String firstName =
rdsDetails.getUser().getFirst_name() != null ? rdsDetails.getUser().getFirst_name() : "";
String lastName =
rdsDetails.getUser().getLast_name() != null ? rdsDetails.getUser().getLast_name() : "";

String username = firstName + ' ' + lastName;

UserViewModel user = new UserViewModel();
user.setId(id);
user.setName(username);

return user;
}

@Override
public SkillViewModel create(CreateSkillViewModel skill) {
if (skillRepository.existsByName(skill.getName())) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.RDS.skilltree.services.external;

import com.RDS.skilltree.viewmodels.RdsUserViewModel;

public interface RdsService {
RdsUserViewModel getUserDetails(String id);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.RDS.skilltree.services.external;

import com.RDS.skilltree.viewmodels.RdsUserViewModel;
import lombok.RequiredArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;

@Service
@RequiredArgsConstructor
public class RdsServiceImplementation implements RdsService {
private static final Logger log = LoggerFactory.getLogger(RdsServiceImplementation.class);
private final RestTemplate restTemplate;

@Value("${rds.backendBaseUrl}")
private String rdsBackendBaseUrl;

@Override
public RdsUserViewModel getUserDetails(String id) {
String url = rdsBackendBaseUrl + "/users?id=" + id;

try {
return restTemplate.getForObject(url, RdsUserViewModel.class);
} catch (RestClientException error) {
log.error("Error calling url: {}, Error : {}", url, error.getMessage());
throw new RuntimeException("Failed to communicate with RDS backend");
}
}
}
Loading

0 comments on commit ba3cf55

Please sign in to comment.