diff --git a/src/main/java/com/provedcode/kudos/controller/KudosController.java b/src/main/java/com/provedcode/kudos/controller/KudosController.java new file mode 100644 index 0000000..101de0c --- /dev/null +++ b/src/main/java/com/provedcode/kudos/controller/KudosController.java @@ -0,0 +1,32 @@ +package com.provedcode.kudos.controller; + +import com.provedcode.kudos.service.KudosService; +import com.provedcode.kudos.model.response.KudosAmount; +import lombok.AllArgsConstructor; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.security.core.Authentication; +import org.springframework.web.bind.annotation.*; + +@RestController +@AllArgsConstructor +@RequestMapping("/api/v3/talent") +public class KudosController { + KudosService kudosService; + + @GetMapping("/proofs/{proof-id}/kudos") + KudosAmount getKudosProof(@PathVariable("proof-id") long id) { + return kudosService.getAmountKudosProof(id); + } + + @PreAuthorize("hasRole('TALENT')") + @PostMapping("/proofs/{proof-id}/kudos") + void addKudosToProof(@PathVariable("proof-id") long id, Authentication authentication) { + kudosService.addKudosToProof(id, authentication); + } + + @PreAuthorize("hasRole('TALENT')") + @DeleteMapping("/proofs/{proof-id}/kudos") + void deleteKudosFromProof(@PathVariable("proof-id") long id, Authentication authentication) { + kudosService.deleteKudosFromProof(id, authentication); + } +} \ No newline at end of file diff --git a/src/main/java/com/provedcode/kudos/model/entity/Kudos.java b/src/main/java/com/provedcode/kudos/model/entity/Kudos.java new file mode 100644 index 0000000..e73bbea --- /dev/null +++ b/src/main/java/com/provedcode/kudos/model/entity/Kudos.java @@ -0,0 +1,28 @@ +package com.provedcode.kudos.model.entity; + +import com.provedcode.talent.model.entity.Talent; +import com.provedcode.talent.model.entity.TalentProof; +import jakarta.persistence.*; +import lombok.*; + +import java.util.List; + +@Getter +@Setter +@Entity +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Table(name = "kudos") +public class Kudos { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id", nullable = false) + private Long id; + @ManyToOne(cascade = CascadeType.ALL) + @JoinColumn(name = "talent_id") + private Talent talent; + @ManyToOne + @JoinColumn(name = "proof_id") + private TalentProof proof; +} \ No newline at end of file diff --git a/src/main/java/com/provedcode/kudos/model/response/KudosAmount.java b/src/main/java/com/provedcode/kudos/model/response/KudosAmount.java new file mode 100644 index 0000000..927357b --- /dev/null +++ b/src/main/java/com/provedcode/kudos/model/response/KudosAmount.java @@ -0,0 +1,6 @@ +package com.provedcode.kudos.model.response; + +public record KudosAmount( + long amount +) { +} \ 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 new file mode 100644 index 0000000..4338d6b --- /dev/null +++ b/src/main/java/com/provedcode/kudos/repository/KudosRepository.java @@ -0,0 +1,12 @@ +package com.provedcode.kudos.repository; + +import com.provedcode.kudos.model.entity.Kudos; +import com.provedcode.talent.model.entity.Talent; +import com.provedcode.talent.model.entity.TalentProof; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface KudosRepository extends JpaRepository { + long countByProof_Id(Long id); + + boolean existsByTalent(Talent talent); +} \ 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 new file mode 100644 index 0000000..79056ae --- /dev/null +++ b/src/main/java/com/provedcode/kudos/service/KudosService.java @@ -0,0 +1,89 @@ +package com.provedcode.kudos.service; + +import com.provedcode.kudos.model.entity.Kudos; +import com.provedcode.kudos.model.response.KudosAmount; +import com.provedcode.kudos.repository.KudosRepository; +import com.provedcode.talent.model.ProofStatus; +import com.provedcode.talent.model.entity.Talent; +import com.provedcode.talent.model.entity.TalentProof; +import com.provedcode.talent.repo.TalentProofRepository; +import com.provedcode.talent.repo.TalentRepository; +import com.provedcode.user.model.entity.UserInfo; +import com.provedcode.user.repo.UserInfoRepository; +import lombok.AllArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.security.core.Authentication; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.server.ResponseStatusException; + +import java.util.List; + +import static org.springframework.http.HttpStatus.*; + +@Service +@AllArgsConstructor +@Transactional +public class KudosService { + KudosRepository kudosRepository; + TalentRepository talentRepository; + TalentProofRepository talentProofRepository; + UserInfoRepository userInfoRepository; + + @Transactional(readOnly = true) + public KudosAmount getAmountKudosProof(long id) { + TalentProof talentProof = talentProofRepository.findById(id) + .orElseThrow(() -> new ResponseStatusException(NOT_FOUND, "proof with id = %s not found".formatted(id))); + + if (!talentProof.getStatus().equals(ProofStatus.PUBLISHED)) { + throw new ResponseStatusException(FORBIDDEN); + } + + long count = kudosRepository.countByProof_Id(id); + return new KudosAmount(count); + } + + public void addKudosToProof(long id, Authentication authentication) { + UserInfo userInfo = userInfoRepository.findByLogin(authentication.getName()) + .orElseThrow(() -> new ResponseStatusException(NOT_FOUND, "Talent with id = %s not found".formatted(id))); + + Talent talent = userInfo.getTalent(); + + TalentProof talentProof = talentProofRepository.findById(id) + .orElseThrow(() -> new ResponseStatusException(NOT_FOUND, "Proof with id = %s not found".formatted(id))); + + if (talent.getId().equals(talentProof.getTalent().getId())) { + throw new ResponseStatusException(FORBIDDEN, "Talent can’t give `kudos` to himself"); + } + if (kudosRepository.existsByTalent(talent)) { + throw new ResponseStatusException(CONFLICT, "Talent can give only one `kudos` for one proof"); + } + if (!talentProof.getStatus().equals(ProofStatus.PUBLISHED)) { + throw new ResponseStatusException(FORBIDDEN); + } + + kudosRepository.save(Kudos.builder() + .proof(talentProof) + .talent(talent) + .build()); + } + + public void deleteKudosFromProof(long id, Authentication authentication) { + UserInfo userInfo = userInfoRepository.findByLogin(authentication.getName()) + .orElseThrow(() -> new ResponseStatusException(NOT_FOUND, "Talent with id = %s not found".formatted(id))); + Talent talent = userInfo.getTalent(); + + TalentProof talentProof = talentProofRepository.findById(id) + .orElseThrow(() -> new ResponseStatusException(NOT_FOUND, "Proof with id = %s not found".formatted(id))); + + if (!kudosRepository.existsByTalent(talent)) { + throw new ResponseStatusException(CONFLICT, "kudos don`t exist"); + } + + Kudos kudos = talent.getCudoses().stream().filter(i -> i.getProof().getId().equals(id)).findFirst().orElseThrow(); + talentProof.getKudos().remove(kudos); + talent.getCudoses().remove(kudos); + + talentRepository.save(talent); + } +} \ No newline at end of file diff --git a/src/main/java/com/provedcode/talent/model/entity/Talent.java b/src/main/java/com/provedcode/talent/model/entity/Talent.java index bd04d3f..8a0f294 100644 --- a/src/main/java/com/provedcode/talent/model/entity/Talent.java +++ b/src/main/java/com/provedcode/talent/model/entity/Talent.java @@ -1,5 +1,6 @@ package com.provedcode.talent.model.entity; +import com.provedcode.kudos.model.entity.Kudos; import jakarta.persistence.*; import jakarta.validation.constraints.NotEmpty; import lombok.*; @@ -36,14 +37,16 @@ public class Talent { private String image; @OneToOne(mappedBy = "talent", cascade = CascadeType.ALL, orphanRemoval = true) private TalentDescription talentDescription; - @OneToMany(fetch = FetchType.EAGER, mappedBy = "talent", cascade = CascadeType.ALL, orphanRemoval = true) + @OneToMany(mappedBy = "talent", cascade = CascadeType.ALL, orphanRemoval = true) private List talentLinks = new ArrayList<>(); - @OneToMany(fetch = FetchType.EAGER, mappedBy = "talent", cascade = CascadeType.ALL, orphanRemoval = true) + @OneToMany(mappedBy = "talent", cascade = CascadeType.ALL, orphanRemoval = true) private List talentTalents = new ArrayList<>(); - @OneToMany(fetch = FetchType.EAGER, mappedBy = "talent", cascade = CascadeType.ALL, orphanRemoval = true) + @OneToMany(mappedBy = "talent", cascade = CascadeType.ALL, orphanRemoval = true) private List talentContacts = new ArrayList<>(); - @OneToMany(fetch = FetchType.EAGER, mappedBy = "talent", cascade = CascadeType.ALL, orphanRemoval = true) + @OneToMany(mappedBy = "talent", cascade = CascadeType.ALL, orphanRemoval = true) private List talentAttachedFiles = new ArrayList<>(); - @OneToMany(fetch = FetchType.EAGER, mappedBy = "talent", cascade = CascadeType.ALL, orphanRemoval = true) + @OneToMany(mappedBy = "talent", cascade = CascadeType.ALL, orphanRemoval = true) private List talentProofs = new ArrayList<>(); + @OneToMany(mappedBy = "talent", cascade = CascadeType.ALL, orphanRemoval = true) + private List cudoses = new ArrayList<>(); } \ No newline at end of file diff --git a/src/main/java/com/provedcode/talent/model/entity/TalentAttachedFile.java b/src/main/java/com/provedcode/talent/model/entity/TalentAttachedFile.java index 36e2e04..b35fdb5 100644 --- a/src/main/java/com/provedcode/talent/model/entity/TalentAttachedFile.java +++ b/src/main/java/com/provedcode/talent/model/entity/TalentAttachedFile.java @@ -17,13 +17,10 @@ public class TalentAttachedFile { @Column(name = "id", nullable = false) private Long id; @NotNull - @Column(name = "talent_id", nullable = false) - private Long talentId; + @ManyToOne + @JoinColumn(name = "talent_id", updatable = false) + private Talent talent; @URL @Column(name = "attached_file", length = 100) private String attachedFile; - @NotNull - @ManyToOne - @JoinColumn(name = "talent_id", insertable = false, updatable = false) - private Talent talent; } \ No newline at end of file diff --git a/src/main/java/com/provedcode/talent/model/entity/TalentContact.java b/src/main/java/com/provedcode/talent/model/entity/TalentContact.java index 83858f4..48a390b 100644 --- a/src/main/java/com/provedcode/talent/model/entity/TalentContact.java +++ b/src/main/java/com/provedcode/talent/model/entity/TalentContact.java @@ -17,12 +17,9 @@ public class TalentContact { @Column(name = "id", nullable = false) private Long id; @NotNull - @Column(name = "talent_id", nullable = false) - private Long talentId; - @Column(name = "contact") - private String contact; - @NotNull @ManyToOne - @JoinColumn(name = "talent_id", insertable = false, updatable = false) + @JoinColumn(name = "talent_id", updatable = false) private Talent talent; + @Column(name = "contact") + private String contact; } \ No newline at end of file diff --git a/src/main/java/com/provedcode/talent/model/entity/TalentDescription.java b/src/main/java/com/provedcode/talent/model/entity/TalentDescription.java index dbf4188..21629d3 100644 --- a/src/main/java/com/provedcode/talent/model/entity/TalentDescription.java +++ b/src/main/java/com/provedcode/talent/model/entity/TalentDescription.java @@ -19,14 +19,11 @@ public class TalentDescription { @Column(nullable = false) private Long id; @NotNull - @Column(name = "talent_id", nullable = false) - private Long talentId; + @OneToOne(orphanRemoval = true) + @JoinColumn(name = "talent_id", updatable = false) + private Talent talent; @Column(name = "BIO") private String bio; @Column(name = "addition_info") private String additionalInfo; - @NotNull - @OneToOne(orphanRemoval = true) - @JoinColumn(name = "talent_id", insertable = false, updatable = false) - private Talent talent; } \ No newline at end of file diff --git a/src/main/java/com/provedcode/talent/model/entity/TalentLink.java b/src/main/java/com/provedcode/talent/model/entity/TalentLink.java index a567f5e..bcb4fac 100644 --- a/src/main/java/com/provedcode/talent/model/entity/TalentLink.java +++ b/src/main/java/com/provedcode/talent/model/entity/TalentLink.java @@ -17,13 +17,10 @@ public class TalentLink { @Column(nullable = false) private Long id; @NotNull - @Column(name = "talent_id", nullable = false) - private Long talentId; + @ManyToOne + @JoinColumn(name = "talent_id", updatable = false) + private Talent talent; @URL @Column(name = "link") private String link; - @NotNull - @ManyToOne - @JoinColumn(name = "talent_id", insertable = false, updatable = false) - private Talent talent; } \ No newline at end of file diff --git a/src/main/java/com/provedcode/talent/model/entity/TalentProof.java b/src/main/java/com/provedcode/talent/model/entity/TalentProof.java index 11e3108..0650310 100644 --- a/src/main/java/com/provedcode/talent/model/entity/TalentProof.java +++ b/src/main/java/com/provedcode/talent/model/entity/TalentProof.java @@ -1,5 +1,6 @@ package com.provedcode.talent.model.entity; +import com.provedcode.kudos.model.entity.Kudos; import com.provedcode.talent.model.ProofStatus; import jakarta.persistence.*; import jakarta.validation.constraints.NotEmpty; @@ -9,6 +10,7 @@ import org.hibernate.validator.constraints.URL; import java.time.LocalDateTime; +import java.util.List; @Accessors(chain = true) @NoArgsConstructor @@ -24,8 +26,9 @@ public class TalentProof { @Column(name = "id", nullable = false) private Long id; @NotNull - @Column(name = "talent_id", nullable = false) - private Long talentId; + @ManyToOne + @JoinColumn(name = "talent_id", updatable = false) + private Talent talent; @NotEmpty @URL @Column(name = "link", length = 100) @@ -36,8 +39,6 @@ public class TalentProof { @Column(length = 20) private ProofStatus status; private LocalDateTime created; - @NotNull - @ManyToOne - @JoinColumn(name = "talent_id", insertable = false, updatable = false) - private Talent talent; + @OneToMany(fetch = FetchType.EAGER, mappedBy = "proof", cascade = CascadeType.ALL, orphanRemoval = true) + private List kudos; } \ No newline at end of file diff --git a/src/main/java/com/provedcode/talent/model/entity/TalentTalents.java b/src/main/java/com/provedcode/talent/model/entity/TalentTalents.java index 0eef149..2c087b0 100644 --- a/src/main/java/com/provedcode/talent/model/entity/TalentTalents.java +++ b/src/main/java/com/provedcode/talent/model/entity/TalentTalents.java @@ -17,12 +17,9 @@ public class TalentTalents { @Column(name = "id", nullable = false) private Long id; @NotNull - @Column(name = "talent_id", nullable = false) - private Long talentId; - @Column(name = "talent_name") - private String talentName; - @NotNull @ManyToOne - @JoinColumn(name = "talent_id", insertable = false, updatable = false) + @JoinColumn(name = "talent_id", updatable = false) private Talent talent; + @Column(name = "talent_name") + private String talentName; } \ No newline at end of file diff --git a/src/main/java/com/provedcode/talent/service/TalentProofService.java b/src/main/java/com/provedcode/talent/service/TalentProofService.java index 0958c77..e4783b4 100644 --- a/src/main/java/com/provedcode/talent/service/TalentProofService.java +++ b/src/main/java/com/provedcode/talent/service/TalentProofService.java @@ -82,7 +82,7 @@ public TalentProof getTalentProof(long proofId, Authentication authentication) { UserInfo userInfo = userInfoRepository.findByLogin(authentication.getName()).orElseThrow( () -> new ResponseStatusException(NOT_FOUND, "user with this token not found")); - if (talentProof.getTalentId().equals(userInfo.getTalentId()) || + if (talentProof.getTalent().getId().equals(userInfo.getTalent().getId()) || talentProof.getStatus().equals(ProofStatus.PUBLISHED)) { return talentProof; } else { @@ -157,7 +157,6 @@ public ResponseEntity addProof(AddProof addProof, long talentId, Authenticati TalentProof talentProof = TalentProof.builder() .talent(talent.get()) - .talentId(talentId) .link(addProof.link()) .text(addProof.text()) .status(ProofStatus.DRAFT) diff --git a/src/main/java/com/provedcode/talent/service/impl/TalentServiceImpl.java b/src/main/java/com/provedcode/talent/service/impl/TalentServiceImpl.java index cbe00cd..29939c7 100644 --- a/src/main/java/com/provedcode/talent/service/impl/TalentServiceImpl.java +++ b/src/main/java/com/provedcode/talent/service/impl/TalentServiceImpl.java @@ -89,7 +89,6 @@ public Talent editTalent(long id, EditTalent editTalent, Authentication authenti .setBio(editTalent.bio() != null ? editTalent.bio() : editableTalentDescription.getBio()); } else { editableTalentDescription = TalentDescription.builder() - .talentId(idEditableTalent) .additionalInfo(editTalent.additionalInfo()) .bio(editTalent.bio()) .talent(editableTalent) @@ -99,7 +98,6 @@ public Talent editTalent(long id, EditTalent editTalent, Authentication authenti if (editTalent.talents() != null) { editableTalentTalents.clear(); editableTalentTalents.addAll(editTalent.talents().stream().map(s -> TalentTalents.builder() - .talentId(idEditableTalent) .talent(editableTalent) .talentName(s) .build()).toList()); @@ -108,7 +106,6 @@ public Talent editTalent(long id, EditTalent editTalent, Authentication authenti if (editTalent.links() != null) { editableTalentLinks.clear(); editableTalentLinks.addAll(editTalent.links().stream().map(l -> TalentLink.builder() - .talentId(idEditableTalent) .talent(editableTalent) .link(l) .build()).toList()); @@ -117,8 +114,6 @@ public Talent editTalent(long id, EditTalent editTalent, Authentication authenti if (editTalent.contacts() != null) { editableTalentContacts.clear(); editableTalentContacts.addAll(editTalent.contacts().stream().map(s -> TalentContact.builder() - .talentId( - idEditableTalent) .talent(editableTalent) .contact(s) .build()).toList()); @@ -127,8 +122,6 @@ public Talent editTalent(long id, EditTalent editTalent, Authentication authenti if (editTalent.attachedFiles() != null) { editableTalentAttachedFile.clear(); editableTalentAttachedFile.addAll(editTalent.attachedFiles().stream().map(s -> TalentAttachedFile.builder() - .talentId( - idEditableTalent) .talent(editableTalent) .attachedFile( s) diff --git a/src/main/java/com/provedcode/talent/utill/ValidateTalentForCompliance.java b/src/main/java/com/provedcode/talent/utill/ValidateTalentForCompliance.java index aad59af..c287f43 100644 --- a/src/main/java/com/provedcode/talent/utill/ValidateTalentForCompliance.java +++ b/src/main/java/com/provedcode/talent/utill/ValidateTalentForCompliance.java @@ -35,7 +35,7 @@ public void userAndProofVerification(Optional talent, if (talentProof.isEmpty()) { throw new ResponseStatusException(NOT_FOUND, String.format("proof with id = %d not found", proofId)); } - if (talentProof.get().getTalentId() != talentId) { + if (talentProof.get().getTalent().getId() != talentId) { throw new ResponseStatusException(FORBIDDEN, "you can`t delete/update another proof"); } } diff --git a/src/main/java/com/provedcode/user/model/entity/UserInfo.java b/src/main/java/com/provedcode/user/model/entity/UserInfo.java index d2b5183..7714b77 100644 --- a/src/main/java/com/provedcode/user/model/entity/UserInfo.java +++ b/src/main/java/com/provedcode/user/model/entity/UserInfo.java @@ -22,8 +22,9 @@ public class UserInfo { @Column(name = "id", nullable = false) private Long id; @NotNull - @Column(name = "talent_id") - private Long talentId; + @OneToOne(cascade = CascadeType.ALL, orphanRemoval = true) + @JoinColumn(name = "talent_id", updatable = false) + private Talent talent; @NotEmpty @NotNull @Column(name = "login", length = 100) @@ -32,9 +33,6 @@ public class UserInfo { @NotNull @Column(name = "password") private String password; - @OneToOne(cascade = CascadeType.ALL, orphanRemoval = true) - @JoinColumn(name = "talent_id", insertable = false, updatable = false) - private Talent talent; @ManyToMany(fetch = FetchType.EAGER) @JoinTable(name = "user_authorities", joinColumns = @JoinColumn(name = "user_id"), diff --git a/src/main/java/com/provedcode/user/service/impl/AuthenticationServiceImpl.java b/src/main/java/com/provedcode/user/service/impl/AuthenticationServiceImpl.java index 469d90b..0346c35 100644 --- a/src/main/java/com/provedcode/user/service/impl/AuthenticationServiceImpl.java +++ b/src/main/java/com/provedcode/user/service/impl/AuthenticationServiceImpl.java @@ -49,7 +49,7 @@ public UserInfoDTO login(String name, Collection aut return UserInfoDTO.builder() .token(generateJWTToken(name, authorities)) - .id(userInfo.getTalentId()) + .id(userInfo.getTalent().getId()) .build(); } @@ -67,7 +67,7 @@ public UserInfoDTO register(RegistrationDTO user) { talentEntityRepository.save(talent); UserInfo userInfo = UserInfo.builder() - .talentId(talent.getId()) + .talent(talent) .login(user.login()) .password(passwordEncoder.encode(user.password())) .authorities(Set.of(authorityRepository.findByAuthority(Role.TALENT).orElseThrow())) diff --git a/src/main/resources/schema.sql b/src/main/resources/schema.sql index 89835fd..dce8134 100644 --- a/src/main/resources/schema.sql +++ b/src/main/resources/schema.sql @@ -1,4 +1,3 @@ - drop table if exists authority cascade ; create table authority ( @@ -94,6 +93,16 @@ create table user_info primary key (id) ); + +drop table if exists kudos cascade; +create table kudos +( + id bigserial not null, + proof_id bigint, + talent_id bigint, + primary key (id) +); + alter table if exists talent_attached_file add constraint FKdtjomr27q99ufe065trf8jr7b foreign key (talent_id) references talent; alter table if exists talent_contact @@ -112,3 +121,7 @@ alter table if exists user_authorities add constraint FKhrxn11h0wl1txiaukxjp01uji foreign key (user_id) references user_info; alter table if exists user_info add constraint FKng34qd4ikmdcwg4f8bcpghar9 foreign key (talent_id) references talent; +alter table if exists kudos + add constraint FKkk086iax3mb3yn50g6q4u4gx9 foreign key (proof_id) references talent_proofs; +alter table if exists kudos + add constraint FKsgluvtc41jxfpn3v6ymv8t39k foreign key (talent_id) references talent; \ No newline at end of file