diff --git a/src/main/java/com/provedcode/config/SecurityConfig.java b/src/main/java/com/provedcode/config/SecurityConfig.java index dff7a62..4459d42 100644 --- a/src/main/java/com/provedcode/config/SecurityConfig.java +++ b/src/main/java/com/provedcode/config/SecurityConfig.java @@ -54,6 +54,7 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti .requestMatchers(antMatcher("/api/*/sponsors/**")).permitAll() .requestMatchers(antMatcher("/api/*/login")).permitAll() .requestMatchers(antMatcher("/api/*/skills")).permitAll() + .requestMatchers(antMatcher("/api/*/proofs/**")).permitAll() .requestMatchers(antMatcher("/error")).permitAll() .requestMatchers(antMatcher("/v3/api-docs/**")).permitAll() // for openAPI .requestMatchers(antMatcher("/swagger-ui/**")).permitAll() // for openAPI diff --git a/src/main/java/com/provedcode/kudos/controller/KudosController.java b/src/main/java/com/provedcode/kudos/controller/KudosController.java index 878474b..f065d39 100644 --- a/src/main/java/com/provedcode/kudos/controller/KudosController.java +++ b/src/main/java/com/provedcode/kudos/controller/KudosController.java @@ -1,5 +1,15 @@ package com.provedcode.kudos.controller; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.security.core.Authentication; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +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.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + import com.provedcode.kudos.model.request.SetAmountKudos; import com.provedcode.kudos.model.response.KudosAmount; import com.provedcode.kudos.model.response.KudosAmountWithSponsor; @@ -7,14 +17,9 @@ import com.provedcode.util.annotations.doc.controller.kudos.GetAmountOfKudosApiDoc; import com.provedcode.util.annotations.doc.controller.kudos.GetKudosForSponsorApiDoc; import com.provedcode.util.annotations.doc.controller.kudos.PostAddKudosToProofApiDoc; + import jakarta.validation.Valid; import lombok.AllArgsConstructor; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.security.core.Authentication; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; - -import java.util.Optional; @RestController @AllArgsConstructor @@ -41,8 +46,20 @@ KudosAmountWithSponsor getProofKudos(@PathVariable("proof-id") long proofId, Aut @PreAuthorize("hasRole('SPONSOR')") @PostMapping("/proofs/{proof-id}/kudos") void addKudosToProof(@PathVariable("proof-id") long proofId, - @RequestBody @Valid SetAmountKudos amount, - Authentication authentication) { + @RequestBody @Valid SetAmountKudos amount, + Authentication authentication) { kudosService.addKudosToProof(proofId, amount, authentication); } + + @PreAuthorize("hasRole('SPONSOR')") + @PostMapping("/proofs/{proof-id}/skills/{skill-id}/kudos") + void addKudosToSkill(@PathVariable("proof-id") long proofId, @PathVariable("skill-id") long skillId, + Authentication authentication, @RequestBody @Valid SetAmountKudos amount) { + kudosService.addKudosToSkill(proofId, skillId, amount, authentication); + } + + @GetMapping("/proofs/{proof-id}/skills/{skill-id}/kudos") + KudosAmount getKudosForSkill(@PathVariable("proof-id") long proofId, @PathVariable("skill-id") long skillId) { + return kudosService.getSkillKudos(proofId, skillId); + } } \ No newline at end of file diff --git a/src/main/java/com/provedcode/kudos/repository/KudosRepository.java b/src/main/java/com/provedcode/kudos/repository/KudosRepository.java index 8f489c9..0408d75 100644 --- a/src/main/java/com/provedcode/kudos/repository/KudosRepository.java +++ b/src/main/java/com/provedcode/kudos/repository/KudosRepository.java @@ -1,10 +1,13 @@ package com.provedcode.kudos.repository; -import com.provedcode.kudos.model.entity.Kudos; +import java.util.List; + import org.springframework.data.jpa.repository.JpaRepository; +import com.provedcode.kudos.model.entity.Kudos; +import com.provedcode.talent.model.entity.ProofSkill; + + public interface KudosRepository extends JpaRepository { -// long countByProofId(Long id); -// -// boolean existsBySponsorIdAndProofId(Long sponsorId, Long proofId); + List findBySkill(ProofSkill skill); } \ No newline at end of file diff --git a/src/main/java/com/provedcode/kudos/service/KudosService.java b/src/main/java/com/provedcode/kudos/service/KudosService.java index 595396e..39ae8b5 100644 --- a/src/main/java/com/provedcode/kudos/service/KudosService.java +++ b/src/main/java/com/provedcode/kudos/service/KudosService.java @@ -1,10 +1,10 @@ package com.provedcode.kudos.service; -import static org.springframework.http.HttpStatus.BAD_REQUEST; import static org.springframework.http.HttpStatus.FORBIDDEN; import static org.springframework.http.HttpStatus.NOT_FOUND; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.stream.Collectors; @@ -23,6 +23,7 @@ import com.provedcode.sponsor.model.entity.Sponsor; import com.provedcode.sponsor.repository.SponsorRepository; import com.provedcode.talent.model.ProofStatus; +import com.provedcode.talent.model.entity.ProofSkill; import com.provedcode.talent.model.entity.Talent; import com.provedcode.talent.model.entity.TalentProof; import com.provedcode.talent.repo.ProofSkillRepository; @@ -37,111 +38,156 @@ @AllArgsConstructor @Transactional public class KudosService { - KudosRepository kudosRepository; - TalentProofRepository talentProofRepository; - UserInfoRepository userInfoRepository; - SponsorRepository sponsorRepository; - TalentRepository talentRepository; - ProofSkillRepository proofSkillRepository; - SponsorMapper sponsorMapper; - - @Transactional(readOnly = true) - public KudosAmount getKudosForSponsor(long sponsorId, Authentication authentication) { - String login = authentication.getName(); - UserInfo userInfo = userInfoRepository.findByLogin(login) - .orElseThrow(() -> new ResponseStatusException(NOT_FOUND, - "User with login = %s not found".formatted( - login))); - if (!userInfo.getSponsor().getId().equals(sponsorId)) { - throw new ResponseStatusException(FORBIDDEN, "Only the account owner can view the number of kudos"); + KudosRepository kudosRepository; + TalentProofRepository talentProofRepository; + UserInfoRepository userInfoRepository; + SponsorRepository sponsorRepository; + TalentRepository talentRepository; + ProofSkillRepository proofSkillRepository; + SponsorMapper sponsorMapper; + + @Transactional(readOnly = true) + public KudosAmount getKudosForSponsor(long sponsorId, Authentication authentication) { + String login = authentication.getName(); + UserInfo userInfo = userInfoRepository.findByLogin(login) + .orElseThrow(() -> new ResponseStatusException(NOT_FOUND, + "User with login = %s not found".formatted(login))); + if (!userInfo.getSponsor().getId().equals(sponsorId)) { + throw new ResponseStatusException(FORBIDDEN, + "Only the account owner can view the number of kudos"); + } + Sponsor sponsor = sponsorRepository.findById(sponsorId).orElseThrow( + () -> new ResponseStatusException(NOT_FOUND, + String.format("Sponsor with id = %d not found", sponsorId))); + return new KudosAmount(sponsor.getAmountKudos()); } - Sponsor sponsor = sponsorRepository.findById(sponsorId).orElseThrow( - () -> new ResponseStatusException(NOT_FOUND, - String.format("Sponsor with id = %d not found", sponsorId))); - return new KudosAmount(sponsor.getAmountKudos()); - } - - @Transactional(readOnly = true) - public KudosAmountWithSponsor getProofKudos(long proofId, Authentication authentication) { - String login = authentication.getName(); - UserInfo userInfo = userInfoRepository.findByLogin(login) - .orElseThrow(() -> new ResponseStatusException(NOT_FOUND, - "User with login = %s not found".formatted(login))); - Talent talent = talentRepository.findById(userInfo.getTalent().getId()) - .orElseThrow(() -> new ResponseStatusException(NOT_FOUND, - "Talent with login = %s not found".formatted(login))); - TalentProof talentProof = talentProofRepository.findById(proofId) - .orElseThrow(() -> new ResponseStatusException(NOT_FOUND, - "Proof with id = %s not found".formatted(proofId))); - - Long countOfAllKudos = talentProof.getProofSkills() - .stream().flatMap(proofSkills -> proofSkills.getKudos() - .stream().map(Kudos::getAmount)) - .reduce(0L, (prev, next) -> prev + next); - - if (talent.getId().equals(talentProof.getTalent().getId())) { - Map> skillsMap = new HashMap<>(); - talentProof.getProofSkills().forEach(proofSkill -> { // I dnk wtf is this piece of shit, but it works. - String skill = proofSkill.getSkill().getSkill(); - Map kudosFromSponsor = talentProof.getProofSkills().stream() - .filter(proofSkills -> proofSkills.getSkill().getSkill().equals(skill)) - .flatMap(proofSkills -> proofSkills.getKudos().stream()) - .collect(Collectors.toMap( - Kudos::getAmount, - proof -> proof.getSponsor() != null - ? sponsorMapper.toDto(proof.getSponsor()) - : SponsorDTO.builder().build(), - (prev, next) -> next, - HashMap::new)); - skillsMap.put(skill, kudosFromSponsor); - }); - return KudosAmountWithSponsor.builder() - .allKudosOnProof(countOfAllKudos) - .kudosFromSponsor(skillsMap) - .build(); - } else { - return KudosAmountWithSponsor.builder() - .allKudosOnProof(countOfAllKudos) - .kudosFromSponsor(null).build(); + + @Transactional(readOnly = true) + public KudosAmountWithSponsor getProofKudos(long proofId, Authentication authentication) { + String login = authentication.getName(); + UserInfo userInfo = userInfoRepository.findByLogin(login) + .orElseThrow(() -> new ResponseStatusException(NOT_FOUND, + "User with login = %s not found".formatted(login))); + Talent talent = talentRepository.findById(userInfo.getTalent().getId()) + .orElseThrow(() -> new ResponseStatusException(NOT_FOUND, + "Talent with login = %s not found".formatted(login))); + TalentProof talentProof = talentProofRepository.findById(proofId) + .orElseThrow(() -> new ResponseStatusException(NOT_FOUND, + "Proof with id = %s not found".formatted(proofId))); + + Long countOfAllKudos = talentProof.getProofSkills() + .stream().flatMap(proofSkills -> proofSkills.getKudos() + .stream().map(Kudos::getAmount)) + .reduce(0L, (prev, next) -> prev + next); + + if (talent.getId().equals(talentProof.getTalent().getId())) { + Map> skillsMap = new HashMap<>(); + talentProof.getProofSkills().forEach(proofSkill -> { // I dnk wtf is this piece of shit, but it + // works. + String skill = proofSkill.getSkill().getSkill(); + Map kudosFromSponsor = talentProof.getProofSkills().stream() + .filter(proofSkills -> proofSkills.getSkill().getSkill().equals(skill)) + .flatMap(proofSkills -> proofSkills.getKudos().stream()) + .collect(Collectors.toMap( + Kudos::getAmount, + proof -> proof.getSponsor() != null + ? sponsorMapper.toDto( + proof.getSponsor()) + : SponsorDTO.builder().build(), + (prev, next) -> next, + HashMap::new)); + skillsMap.put(skill, kudosFromSponsor); + }); + return KudosAmountWithSponsor.builder() + .allKudosOnProof(countOfAllKudos) + .kudosFromSponsor(skillsMap) + .build(); + } else { + return KudosAmountWithSponsor.builder() + .allKudosOnProof(countOfAllKudos) + .kudosFromSponsor(null).build(); + } } - } - - - public void addKudosToProof(long proofId, SetAmountKudos amountOfKudoses, Authentication authentication) { - String login = authentication.getName(); - UserInfo userInfo = userInfoRepository.findByLogin(login) - .orElseThrow(() -> new ResponseStatusException(NOT_FOUND, - "User with login = %s not found".formatted( - login))); - Sponsor sponsor = sponsorRepository.findById(userInfo.getSponsor().getId()).orElseThrow( - () -> new ResponseStatusException(NOT_FOUND, - "Sponsor with login = %s not found".formatted(login))); - TalentProof talentProof = talentProofRepository.findById(proofId) - .orElseThrow(() -> new ResponseStatusException(NOT_FOUND, - "Proof with id = %d not found".formatted( - proofId))); - if (!talentProof.getStatus().equals(ProofStatus.PUBLISHED)) - throw new ResponseStatusException(FORBIDDEN, "Proof that was kudosed does not have the PUBLISHED status"); - long obtainedAmount = amountOfKudoses.amount(); - - if (sponsor.getAmountKudos() < obtainedAmount) { - throw new ResponseStatusException(FORBIDDEN, "The sponsor cannot give more kudos than he has"); + + public void addKudosToProof(long proofId, SetAmountKudos amountOfKudoses, Authentication authentication) { + String login = authentication.getName(); + UserInfo userInfo = userInfoRepository.findByLogin(login) + .orElseThrow(() -> new ResponseStatusException(NOT_FOUND, + "User with login = %s not found".formatted(login))); + Sponsor sponsor = sponsorRepository.findById(userInfo.getSponsor().getId()).orElseThrow( + () -> new ResponseStatusException(NOT_FOUND, + "Sponsor with login = %s not found".formatted(login))); + TalentProof talentProof = talentProofRepository.findById(proofId) + .orElseThrow(() -> new ResponseStatusException(NOT_FOUND, + "Proof with id = %d not found".formatted(proofId))); + if (!talentProof.getStatus().equals(ProofStatus.PUBLISHED)) + throw new ResponseStatusException(FORBIDDEN, + "Proof that was kudosed does not have the PUBLISHED status"); + long obtainedAmount = amountOfKudoses.amount(); + + if (sponsor.getAmountKudos() < obtainedAmount) { + throw new ResponseStatusException(FORBIDDEN, "The sponsor cannot give more kudos than he has"); + } + Long modula = obtainedAmount % talentProof.getProofSkills().size(); + if (modula != 0) { + obtainedAmount -= modula; + } + sponsor.setAmountKudos(sponsor.getAmountKudos() - obtainedAmount); + + Long addKudoses = obtainedAmount / talentProof.getProofSkills().size(); + + talentProof.getProofSkills().forEach(proofSkill -> { + Kudos kudos = Kudos.builder() + .sponsor(sponsor) + .skill(proofSkill) + .amount(addKudoses) + .build(); + proofSkill.getKudos().add(kudosRepository.save(kudos)); + }); } - Long modula = obtainedAmount % talentProof.getProofSkills().size(); - if (modula != 0) { - obtainedAmount -= modula; + + public void addKudosToSkill(long proofId, long skillId, SetAmountKudos amountOfKudos, + Authentication authentication) { + String login = authentication.getName(); + UserInfo userInfo = userInfoRepository.findByLogin(login) + .orElseThrow(() -> new ResponseStatusException(NOT_FOUND, + "User with login = %s not found".formatted( + login))); + Sponsor sponsor = sponsorRepository.findById(userInfo.getSponsor().getId()).orElseThrow( + () -> new ResponseStatusException(NOT_FOUND, + "Sponsor with login = %s not found".formatted(login))); + TalentProof talentProof = talentProofRepository.findById(proofId) + .orElseThrow(() -> new ResponseStatusException(NOT_FOUND, + "Proof with id = %d not found".formatted(proofId))); + if (!talentProof.getStatus().equals(ProofStatus.PUBLISHED)) + throw new ResponseStatusException(FORBIDDEN, + "Skill on proof that was kudosed does not have the PUBLISHED status"); + long obtainedAmount = amountOfKudos.amount(); + if (sponsor.getAmountKudos() < obtainedAmount) { + throw new ResponseStatusException(FORBIDDEN, "The sponsor cannot give more kudos than he has"); + } + sponsor.setAmountKudos(sponsor.getAmountKudos() - obtainedAmount); + ProofSkill proofSkill = talentProof.getProofSkills().stream() + .filter(s -> s.getSkill().getId().equals(skillId)) + .findFirst().orElseThrow(() -> new ResponseStatusException(NOT_FOUND, + "Skill with id = %d not found".formatted(skillId))); + kudosRepository.save(Kudos.builder().amount(obtainedAmount).sponsor(sponsor).skill(proofSkill).build()); + } + + @Transactional(readOnly = true) + public KudosAmount getSkillKudos(long proofId, long skillId) { + TalentProof talentProof = talentProofRepository.findById(proofId) + .orElseThrow(() -> new ResponseStatusException(NOT_FOUND, + "Proof with id = %d not found".formatted(proofId))); + if (!talentProof.getStatus().equals(ProofStatus.PUBLISHED)) + throw new ResponseStatusException(FORBIDDEN, + "The skill from the proof that was referred to does not have a PUBLISHED status"); + ProofSkill proofSkill = talentProof.getProofSkills().stream() + .filter(s -> s.getSkill().getId().equals(skillId)) + .findFirst().orElseThrow(() -> new ResponseStatusException(NOT_FOUND, + "Skill with id = %d not found".formatted(skillId))); + List kudos = kudosRepository.findBySkill(proofSkill); + long amountOfKudos = kudos.stream().map(Kudos::getAmount).reduce(0L, Long::sum); + return new KudosAmount(amountOfKudos); } - sponsor.setAmountKudos(sponsor.getAmountKudos() - obtainedAmount); - - Long addKudoses = obtainedAmount / talentProof.getProofSkills().size(); - - talentProof.getProofSkills().forEach(proofSkill -> { - Kudos kudos = Kudos.builder() - .sponsor(sponsor) - .skill(proofSkill) - .amount(addKudoses) - .build(); - proofSkill.getKudos().add(kudosRepository.save(kudos)); - }); - } } diff --git a/src/main/resources/application-dev.properties b/src/main/resources/application-dev.properties index 60dd6cf..21ea24c 100644 --- a/src/main/resources/application-dev.properties +++ b/src/main/resources/application-dev.properties @@ -1,6 +1,6 @@ spring.datasource.driver-class-name=org.h2.Driver spring.datasource.username=sa spring.datasource.url=jdbc:h2:mem:./testdb;MODE=PostgreSQL;DATABASE_TO_LOWER=TRUE;DEFAULT_NULL_ORDERING=HIGH -#logging.level.web=DEBUG -#logging.level.sql=DEBUG -spring.jpa.show-sql=true \ No newline at end of file +# logging.level.web=DEBUG +# logging.level.sql=DEBUG +# spring.jpa.show-sql=true diff --git a/src/main/resources/db/changelog/changeset/V5/talent-schema-V5.sql b/src/main/resources/db/changelog/changeset/V5/talent-schema-V5.sql index 5871498..2eca374 100644 --- a/src/main/resources/db/changelog/changeset/V5/talent-schema-V5.sql +++ b/src/main/resources/db/changelog/changeset/V5/talent-schema-V5.sql @@ -47,7 +47,7 @@ CREATE TABLE proofs ( ); CREATE TABLE skills ( id BIGINT GENERATED BY DEFAULT AS IDENTITY NOT NULL, - skill VARCHAR(30), + skill VARCHAR(50), PRIMARY KEY (id) ); CREATE TABLE talents_skills (