From cec78c5a84ade5ff6e61d72051cd0d894b1bca49 Mon Sep 17 00:00:00 2001 From: Denis Boyko Date: Sun, 21 May 2023 17:51:14 +0300 Subject: [PATCH 01/19] refactor userInfo: add uuid and isLocked, add DeletedUser --- .../user/model/entity/DeletedUser.java | 27 +++++++++++++++++++ .../user/model/entity/UserInfo.java | 2 ++ 2 files changed, 29 insertions(+) create mode 100644 src/main/java/com/provedcode/user/model/entity/DeletedUser.java diff --git a/src/main/java/com/provedcode/user/model/entity/DeletedUser.java b/src/main/java/com/provedcode/user/model/entity/DeletedUser.java new file mode 100644 index 0000000..cb57915 --- /dev/null +++ b/src/main/java/com/provedcode/user/model/entity/DeletedUser.java @@ -0,0 +1,27 @@ +package com.provedcode.user.model.entity; + +import com.provedcode.sponsor.model.entity.Sponsor; +import com.provedcode.talent.model.entity.Talent; +import jakarta.persistence.*; +import lombok.*; +import lombok.extern.slf4j.Slf4j; + +import java.time.Instant; + +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Setter +@Slf4j +@Builder +@Entity +public class DeletedUser { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + private Instant timeToDelete; + @OneToOne + @JoinColumn(name = "users_info_id", referencedColumnName = "users_info_id") + private UserInfo deletedUser; + +} \ No newline at end of file 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 19dc0da..33ee020 100644 --- a/src/main/java/com/provedcode/user/model/entity/UserInfo.java +++ b/src/main/java/com/provedcode/user/model/entity/UserInfo.java @@ -55,4 +55,6 @@ public class UserInfo { @ManyToMany(fetch = FetchType.EAGER) @JoinTable(name = "users_authorities", joinColumns = @JoinColumn(name = "user_id"), inverseJoinColumns = @JoinColumn(name = "authority_id")) private Set authorities = new LinkedHashSet<>(); + private String uuid; + private Boolean isLocked; } \ No newline at end of file From 4f524ee1311b06b61920b47267bc83e38d581214 Mon Sep 17 00:00:00 2001 From: Denis Boyko Date: Sun, 21 May 2023 18:00:49 +0300 Subject: [PATCH 02/19] add deleted-user-schema.sql and refactor user-info-schema-V5.sql --- .../com/provedcode/user/model/entity/DeletedUser.java | 3 +-- .../db/changelog/changeset/V5/deleted-user-schema.sql | 9 +++++++++ .../db/changelog/changeset/V5/user-info-schema-V5.sql | 2 ++ src/main/resources/db/changelog/db.changelog-master.yaml | 2 ++ 4 files changed, 14 insertions(+), 2 deletions(-) create mode 100644 src/main/resources/db/changelog/changeset/V5/deleted-user-schema.sql diff --git a/src/main/java/com/provedcode/user/model/entity/DeletedUser.java b/src/main/java/com/provedcode/user/model/entity/DeletedUser.java index cb57915..f651356 100644 --- a/src/main/java/com/provedcode/user/model/entity/DeletedUser.java +++ b/src/main/java/com/provedcode/user/model/entity/DeletedUser.java @@ -21,7 +21,6 @@ public class DeletedUser { private Long id; private Instant timeToDelete; @OneToOne - @JoinColumn(name = "users_info_id", referencedColumnName = "users_info_id") + @JoinColumn(name = "id", referencedColumnName = "id") private UserInfo deletedUser; - } \ No newline at end of file diff --git a/src/main/resources/db/changelog/changeset/V5/deleted-user-schema.sql b/src/main/resources/db/changelog/changeset/V5/deleted-user-schema.sql new file mode 100644 index 0000000..a5b0514 --- /dev/null +++ b/src/main/resources/db/changelog/changeset/V5/deleted-user-schema.sql @@ -0,0 +1,9 @@ +CREATE TABLE deleted_user +( + id BIGINT GENERATED BY DEFAULT AS IDENTITY NOT NULL, + time_to_delete TIMESTAMP WITHOUT TIME ZONE, + CONSTRAINT pk_deleteduser PRIMARY KEY (id) +); + +ALTER TABLE deleted_user + ADD CONSTRAINT FK_DELETEDUSER_ON_ID FOREIGN KEY (id) REFERENCES users_info (id); \ No newline at end of file diff --git a/src/main/resources/db/changelog/changeset/V5/user-info-schema-V5.sql b/src/main/resources/db/changelog/changeset/V5/user-info-schema-V5.sql index 97b1109..00d7ef5 100644 --- a/src/main/resources/db/changelog/changeset/V5/user-info-schema-V5.sql +++ b/src/main/resources/db/changelog/changeset/V5/user-info-schema-V5.sql @@ -8,6 +8,8 @@ CREATE TABLE users_info ( sponsor_id BIGINT REFERENCES sponsors, login VARCHAR(100) NOT NULL, password VARCHAR(255) NOT NULL, + uuid VARCHAR(255), + is_locked BOOLEAN, PRIMARY KEY (id) ); CREATE TABLE authorities ( diff --git a/src/main/resources/db/changelog/db.changelog-master.yaml b/src/main/resources/db/changelog/db.changelog-master.yaml index 77eb9b2..6e366a2 100644 --- a/src/main/resources/db/changelog/db.changelog-master.yaml +++ b/src/main/resources/db/changelog/db.changelog-master.yaml @@ -27,3 +27,5 @@ databaseChangeLog: file: db/changelog/changeset/V5/dataSkill-V5.sql - include: file: db/changelog/changeset/V5/dataKudos-V5.sql + - include: + file: db/changelog/changeset/V5/deleted-user-schema.sql From b69e2b0d7d8bd478d9da56a1101b29afa4bc5148 Mon Sep 17 00:00:00 2001 From: Denis Boyko Date: Sun, 21 May 2023 18:03:34 +0300 Subject: [PATCH 03/19] refactor register in AuthenticationServiceImpl --- .../user/service/impl/AuthenticationServiceImpl.java | 5 +++++ 1 file changed, 5 insertions(+) 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 dbcce3e..3893dba 100644 --- a/src/main/java/com/provedcode/user/service/impl/AuthenticationServiceImpl.java +++ b/src/main/java/com/provedcode/user/service/impl/AuthenticationServiceImpl.java @@ -30,6 +30,7 @@ import java.time.Instant; import java.util.Collection; import java.util.Set; +import java.util.UUID; import java.util.function.BiFunction; import java.util.stream.Collectors; @@ -80,6 +81,8 @@ public UserInfoDTO register(TalentRegistrationDTO user) { .login(user.login()) .password(passwordEncoder.encode(user.password())) .authorities(Set.of(authorityRepository.findByAuthority(Role.TALENT).orElseThrow())) + .uuid(UUID.randomUUID().toString()) + .isLocked(false) .build(); userInfoRepository.save(userInfo); @@ -111,6 +114,8 @@ public UserInfoDTO register(SponsorRegistrationDTO user) { .password(passwordEncoder.encode(user.password())) .authorities( Set.of(authorityRepository.findByAuthority(Role.SPONSOR).orElseThrow())) + .uuid(UUID.randomUUID().toString()) + .isLocked(false) .build(); userInfoRepository.save(userInfo); From 516a3ab3e75b54f17684b0b70264200a518137bd Mon Sep 17 00:00:00 2001 From: Denis Boyko Date: Sun, 21 May 2023 18:06:25 +0300 Subject: [PATCH 04/19] refactor application.properties --- src/main/resources/application.properties | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index f91aa56..b1c9f41 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -38,4 +38,16 @@ page-config.default-page-num=0 page-config.default-page-size=5 page-config.default-sort-by=created ## -spring.liquibase.enabled=true \ No newline at end of file +spring.liquibase.enabled=true +## +spring.mail.host=smtp.gmail.com +spring.mail.port=587 +#spring.mail.username=${EMAIL_USER} +#spring.mail.password=${EMAIL_PASSWORD} +spring.mail.username=denisboyko702@gmail.com +spring.mail.password=bhbsggqqtpvvnipp +spring.mail.properties.mail.smtp.auth=true +spring.mail.properties.mail.smtp.starttls.enable=true +# Default email response +default-email.user-deleted=Your account has been suspended, it will be deleted after 24 hours, click on the link to restore it:\n%s\nBest wishes from the test team. +default-email.user-deleted-subject=Your account has been suspended, TestTeam. \ No newline at end of file From 3f848154a13de013e985788b1eb4ebb0e01e54e0 Mon Sep 17 00:00:00 2001 From: Denis Boyko Date: Sun, 21 May 2023 18:11:32 +0300 Subject: [PATCH 05/19] add EmailDefaultProps --- .../provedcode/config/EmailDefaultProps.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 src/main/java/com/provedcode/config/EmailDefaultProps.java diff --git a/src/main/java/com/provedcode/config/EmailDefaultProps.java b/src/main/java/com/provedcode/config/EmailDefaultProps.java new file mode 100644 index 0000000..f6adf63 --- /dev/null +++ b/src/main/java/com/provedcode/config/EmailDefaultProps.java @@ -0,0 +1,19 @@ +package com.provedcode.config; + +import jakarta.annotation.PostConstruct; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.validation.annotation.Validated; + +@Validated +@ConfigurationProperties(prefix = "default-email") +@Slf4j +public record EmailDefaultProps( + String userDeleted, + String userDeletedSubject +) { + @PostConstruct + void logging() { + log.info("email-default-props = {}", this); + } +} From 7b28138a9472edcd5b5de6772241c11401281779 Mon Sep 17 00:00:00 2001 From: Denis Boyko Date: Sun, 21 May 2023 18:16:20 +0300 Subject: [PATCH 06/19] add GlobalControllerAdvice --- .../handlers/GlobalControllerAdvice.java | 33 +++++++++++++++++++ .../user/repo/DeletedUserRepository.java | 7 ++++ 2 files changed, 40 insertions(+) create mode 100644 src/main/java/com/provedcode/handlers/GlobalControllerAdvice.java create mode 100644 src/main/java/com/provedcode/user/repo/DeletedUserRepository.java diff --git a/src/main/java/com/provedcode/handlers/GlobalControllerAdvice.java b/src/main/java/com/provedcode/handlers/GlobalControllerAdvice.java new file mode 100644 index 0000000..1d59e1d --- /dev/null +++ b/src/main/java/com/provedcode/handlers/GlobalControllerAdvice.java @@ -0,0 +1,33 @@ +package com.provedcode.handlers; + +import com.provedcode.user.model.entity.UserInfo; +import com.provedcode.user.repo.UserInfoRepository; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.core.Authentication; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.server.ResponseStatusException; + +import static org.springframework.http.HttpStatus.FORBIDDEN; +import static org.springframework.http.HttpStatus.NOT_FOUND; + +@ControllerAdvice +@AllArgsConstructor +@Slf4j +public class GlobalControllerAdvice { + UserInfoRepository userInfoRepository; + + @ModelAttribute + public void handleAuthentication(Authentication authentication) { + if (authentication != null) { + String login = authentication.getName(); + UserInfo user = userInfoRepository.findByLogin(login) + .orElseThrow(() -> new ResponseStatusException(NOT_FOUND, + "User with login {%s} not found".formatted(login))); + if (Boolean.TRUE.equals(user.getIsLocked())) { + throw new ResponseStatusException(FORBIDDEN, "your account is blocked"); + } + } + } +} diff --git a/src/main/java/com/provedcode/user/repo/DeletedUserRepository.java b/src/main/java/com/provedcode/user/repo/DeletedUserRepository.java new file mode 100644 index 0000000..5112167 --- /dev/null +++ b/src/main/java/com/provedcode/user/repo/DeletedUserRepository.java @@ -0,0 +1,7 @@ +package com.provedcode.user.repo; + +import com.provedcode.user.model.entity.DeletedUser; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface DeletedUserRepository extends JpaRepository { +} \ No newline at end of file From 1f3ad71c5c1f86cbd044958bbbb72582209b0d0b Mon Sep 17 00:00:00 2001 From: Denis Boyko Date: Sun, 21 May 2023 18:18:15 +0300 Subject: [PATCH 07/19] add EmailConfig --- .../java/com/provedcode/config/EmailConfig.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 src/main/java/com/provedcode/config/EmailConfig.java diff --git a/src/main/java/com/provedcode/config/EmailConfig.java b/src/main/java/com/provedcode/config/EmailConfig.java new file mode 100644 index 0000000..d1a0073 --- /dev/null +++ b/src/main/java/com/provedcode/config/EmailConfig.java @@ -0,0 +1,15 @@ +package com.provedcode.config; + +import lombok.AllArgsConstructor; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; + +@Configuration +@AllArgsConstructor +public class EmailConfig { + private Environment env; + + public String getDefaultEmail() { + return env.getProperty("EMAIL_USER"); + } +} \ No newline at end of file From c2b67e129ff84c116eb05b0e4cbe90f415531ede Mon Sep 17 00:00:00 2001 From: Denis Boyko Date: Sun, 21 May 2023 18:23:58 +0300 Subject: [PATCH 08/19] add EmailService and dependency --- pom.xml | 4 +++ .../user/service/impl/EmailService.java | 25 +++++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 src/main/java/com/provedcode/user/service/impl/EmailService.java diff --git a/pom.xml b/pom.xml index 85cb069..409f5d0 100644 --- a/pom.xml +++ b/pom.xml @@ -17,6 +17,10 @@ 17 + + org.springframework.boot + spring-boot-starter-mail + org.springdoc diff --git a/src/main/java/com/provedcode/user/service/impl/EmailService.java b/src/main/java/com/provedcode/user/service/impl/EmailService.java new file mode 100644 index 0000000..c497427 --- /dev/null +++ b/src/main/java/com/provedcode/user/service/impl/EmailService.java @@ -0,0 +1,25 @@ +package com.provedcode.user.service.impl; + +import com.provedcode.config.EmailConfig; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.mail.SimpleMailMessage; +import org.springframework.mail.javamail.JavaMailSender; +import org.springframework.stereotype.Service; + +@Service +@AllArgsConstructor +@Slf4j +public class EmailService { + private JavaMailSender javaMailSender; + private EmailConfig emailConfig; + + public void sendEmail(String to, String subject, String text) { + SimpleMailMessage message = new SimpleMailMessage(); + message.setFrom(emailConfig.getDefaultEmail()); + message.setTo(to); + message.setSubject(subject); + message.setText(text); + javaMailSender.send(message); + } +} From a7833a2a32d50c830e02528612d7b74f163aaff1 Mon Sep 17 00:00:00 2001 From: Denis Boyko Date: Sun, 21 May 2023 18:26:18 +0300 Subject: [PATCH 09/19] add ServerInfoConfig --- .../provedcode/config/ServerInfoConfig.java | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 src/main/java/com/provedcode/config/ServerInfoConfig.java diff --git a/src/main/java/com/provedcode/config/ServerInfoConfig.java b/src/main/java/com/provedcode/config/ServerInfoConfig.java new file mode 100644 index 0000000..ba483f4 --- /dev/null +++ b/src/main/java/com/provedcode/config/ServerInfoConfig.java @@ -0,0 +1,29 @@ +package com.provedcode.config; + +import lombok.AllArgsConstructor; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; + +import java.net.InetAddress; +import java.net.UnknownHostException; + +@AllArgsConstructor +@Configuration +public class ServerInfoConfig { + private Environment env; + + public String getServerPort() { + return env.getProperty("server.port"); + } + public String getIpAddress() { + InetAddress ip = null; + try { + return ip.getLocalHost().getHostAddress(); + } catch (UnknownHostException e) { + throw new RuntimeException(e); + } + } + public String getFullServerAddress() { + return getIpAddress() + ":" + getServerPort(); + } +} From e24bc5703af4d91f68d4bd830c6006c1cdde636e Mon Sep 17 00:00:00 2001 From: Denis Boyko Date: Sun, 21 May 2023 19:14:37 +0300 Subject: [PATCH 10/19] refactor deleteTalentById, add email --- .../com/provedcode/config/SecurityConfig.java | 2 ++ .../talent/service/TalentService.java | 27 +++++++++++++++---- .../controller/AuthenticationController.java | 6 +++++ .../user/repo/DeletedUserRepository.java | 3 +++ .../user/repo/UserInfoRepository.java | 1 + .../user/service/AuthenticationService.java | 4 +++ .../impl/AuthenticationServiceImpl.java | 10 +++++++ 7 files changed, 48 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/provedcode/config/SecurityConfig.java b/src/main/java/com/provedcode/config/SecurityConfig.java index dff7a62..c2ee16f 100644 --- a/src/main/java/com/provedcode/config/SecurityConfig.java +++ b/src/main/java/com/provedcode/config/SecurityConfig.java @@ -9,6 +9,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.security.config.Customizer; import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; @@ -58,6 +59,7 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti .requestMatchers(antMatcher("/v3/api-docs/**")).permitAll() // for openAPI .requestMatchers(antMatcher("/swagger-ui/**")).permitAll() // for openAPI .requestMatchers(antMatcher("/swagger-ui.html")).permitAll() // for openAPI + .requestMatchers(antMatcher(HttpMethod.GET, "/api/activate")).permitAll()// for email account recovery .anyRequest().authenticated() ); diff --git a/src/main/java/com/provedcode/talent/service/TalentService.java b/src/main/java/com/provedcode/talent/service/TalentService.java index d89b82d..eb4618d 100644 --- a/src/main/java/com/provedcode/talent/service/TalentService.java +++ b/src/main/java/com/provedcode/talent/service/TalentService.java @@ -1,10 +1,11 @@ package com.provedcode.talent.service; +import com.provedcode.config.EmailDefaultProps; import com.provedcode.config.PageProperties; +import com.provedcode.config.ServerInfoConfig; import com.provedcode.kudos.model.entity.Kudos; import com.provedcode.talent.mapper.TalentProofMapper; import com.provedcode.talent.model.dto.ProofDTO; -import com.provedcode.talent.model.dto.SkillDTO; import com.provedcode.talent.model.dto.SkillIdDTO; import com.provedcode.talent.model.dto.StatisticsDTO; import com.provedcode.talent.model.entity.*; @@ -14,9 +15,12 @@ import com.provedcode.talent.repo.TalentRepository; import com.provedcode.talent.utill.ValidateTalentForCompliance; import com.provedcode.user.model.dto.SessionInfoDTO; +import com.provedcode.user.model.entity.DeletedUser; import com.provedcode.user.model.entity.UserInfo; import com.provedcode.user.repo.AuthorityRepository; +import com.provedcode.user.repo.DeletedUserRepository; import com.provedcode.user.repo.UserInfoRepository; +import com.provedcode.user.service.impl.EmailService; import jakarta.validation.constraints.Max; import jakarta.validation.constraints.Min; import jakarta.validation.constraints.PositiveOrZero; @@ -29,6 +33,8 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.web.server.ResponseStatusException; +import java.time.Instant; +import java.time.temporal.ChronoUnit; import java.util.*; import java.util.stream.Collectors; @@ -47,6 +53,10 @@ public class TalentService { ValidateTalentForCompliance validateTalentForCompliance; SkillsRepository skillsRepository; TalentProofMapper talentProofMapper; + DeletedUserRepository deletedUserRepository; + ServerInfoConfig serverInfoConfig; + EmailService emailService; + EmailDefaultProps emailDefaultProps; @Transactional(readOnly = true) public Page getTalentsPage(Integer page, Integer size) { @@ -143,11 +153,18 @@ public SessionInfoDTO deleteTalentById(long id, Authentication authentication) { validateTalentForCompliance.userVerification(talent, userInfo, id); UserInfo user = userInfo.get(); - Talent entity = talent.get(); + user.setIsLocked(true); + DeletedUser deletedUser = DeletedUser.builder() + .deletedUser(user) + .timeToDelete(Instant.now().plus(3, ChronoUnit.DAYS)) + .build(); + deletedUserRepository.save(deletedUser); + userInfoRepository.save(user); - user.getAuthorities().clear(); - userInfoRepository.delete(user); - talentRepository.delete(entity); + String userActivateAccountLink = serverInfoConfig.getFullServerAddress() + "/api/activate?uuid=" + user.getUuid(); + emailService.sendEmail(user.getLogin(), + emailDefaultProps.userDeletedSubject(), + emailDefaultProps.userDeleted().formatted(userActivateAccountLink)); return new SessionInfoDTO("deleted", "null"); } diff --git a/src/main/java/com/provedcode/user/controller/AuthenticationController.java b/src/main/java/com/provedcode/user/controller/AuthenticationController.java index b30ee13..9377cf6 100644 --- a/src/main/java/com/provedcode/user/controller/AuthenticationController.java +++ b/src/main/java/com/provedcode/user/controller/AuthenticationController.java @@ -1,5 +1,6 @@ package com.provedcode.user.controller; +import com.provedcode.talent.service.TalentService; import com.provedcode.user.model.dto.SponsorRegistrationDTO; import com.provedcode.user.model.dto.TalentRegistrationDTO; import com.provedcode.user.model.dto.UserInfoDTO; @@ -40,4 +41,9 @@ UserInfoDTO register(@RequestBody @Valid TalentRegistrationDTO user) { UserInfoDTO register(@RequestBody @Valid SponsorRegistrationDTO user) { return authenticationService.register(user); } + + @GetMapping("/activate") + void activateAccount(@RequestParam("uuid") String uuid) { + authenticationService.activateAccount(uuid); + } } \ No newline at end of file diff --git a/src/main/java/com/provedcode/user/repo/DeletedUserRepository.java b/src/main/java/com/provedcode/user/repo/DeletedUserRepository.java index 5112167..666a9a4 100644 --- a/src/main/java/com/provedcode/user/repo/DeletedUserRepository.java +++ b/src/main/java/com/provedcode/user/repo/DeletedUserRepository.java @@ -1,7 +1,10 @@ package com.provedcode.user.repo; import com.provedcode.user.model.entity.DeletedUser; +import com.provedcode.user.model.entity.UserInfo; import org.springframework.data.jpa.repository.JpaRepository; public interface DeletedUserRepository extends JpaRepository { + long deleteByDeletedUser(UserInfo deletedUser); + } \ No newline at end of file diff --git a/src/main/java/com/provedcode/user/repo/UserInfoRepository.java b/src/main/java/com/provedcode/user/repo/UserInfoRepository.java index 0b1e73d..0e97885 100644 --- a/src/main/java/com/provedcode/user/repo/UserInfoRepository.java +++ b/src/main/java/com/provedcode/user/repo/UserInfoRepository.java @@ -9,5 +9,6 @@ public interface UserInfoRepository extends JpaRepository { long deleteByTalentId(Long talentId); boolean existsByLogin(String login); Optional findByLogin(String login); + Optional findByUuid(String uuid); } \ No newline at end of file diff --git a/src/main/java/com/provedcode/user/service/AuthenticationService.java b/src/main/java/com/provedcode/user/service/AuthenticationService.java index 57b7bcd..277af5d 100644 --- a/src/main/java/com/provedcode/user/service/AuthenticationService.java +++ b/src/main/java/com/provedcode/user/service/AuthenticationService.java @@ -9,6 +9,10 @@ public interface AuthenticationService { UserInfoDTO login(String name, Collection authorities); + UserInfoDTO register(TalentRegistrationDTO user); + UserInfoDTO register(SponsorRegistrationDTO user); + + void activateAccount(String uuid); } 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 3893dba..cab4ce1 100644 --- a/src/main/java/com/provedcode/user/service/impl/AuthenticationServiceImpl.java +++ b/src/main/java/com/provedcode/user/service/impl/AuthenticationServiceImpl.java @@ -11,6 +11,7 @@ import com.provedcode.user.model.entity.Authority; import com.provedcode.user.model.entity.UserInfo; import com.provedcode.user.repo.AuthorityRepository; +import com.provedcode.user.repo.DeletedUserRepository; import com.provedcode.user.repo.UserInfoRepository; import com.provedcode.user.service.AuthenticationService; import lombok.AllArgsConstructor; @@ -48,6 +49,7 @@ public class AuthenticationServiceImpl implements AuthenticationService { SponsorRepository sponsorRepository; AuthorityRepository authorityRepository; PasswordEncoder passwordEncoder; + DeletedUserRepository deletedUserRepository; @Transactional(readOnly = true) public UserInfoDTO login(String name, Collection authorities) { @@ -145,4 +147,12 @@ private Jwt generateJWTToken(Long id, String login, Collection new ResponseStatusException(NOT_FOUND)); + user.setIsLocked(false); + deletedUserRepository.deleteByDeletedUser(user); + userInfoRepository.save(user); + } } \ No newline at end of file From cdd641c4200c5ecea75edccb0cb1e814291800d0 Mon Sep 17 00:00:00 2001 From: Denis Boyko Date: Sun, 21 May 2023 19:34:17 +0300 Subject: [PATCH 11/19] refactor deleteSponsor, add email account recovery --- .../sponsor/service/SponsorService.java | 44 +++++++++++++++---- 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/provedcode/sponsor/service/SponsorService.java b/src/main/java/com/provedcode/sponsor/service/SponsorService.java index 8beaba7..efcec1d 100644 --- a/src/main/java/com/provedcode/sponsor/service/SponsorService.java +++ b/src/main/java/com/provedcode/sponsor/service/SponsorService.java @@ -1,13 +1,18 @@ package com.provedcode.sponsor.service; +import com.provedcode.config.EmailDefaultProps; import com.provedcode.config.PageProperties; +import com.provedcode.config.ServerInfoConfig; import com.provedcode.kudos.model.entity.Kudos; import com.provedcode.sponsor.model.entity.Sponsor; import com.provedcode.sponsor.model.request.EditSponsor; import com.provedcode.sponsor.repository.SponsorRepository; import com.provedcode.sponsor.utill.ValidateSponsorForCompliance; +import com.provedcode.user.model.entity.DeletedUser; import com.provedcode.user.model.entity.UserInfo; +import com.provedcode.user.repo.DeletedUserRepository; import com.provedcode.user.repo.UserInfoRepository; +import com.provedcode.user.service.impl.EmailService; import lombok.AllArgsConstructor; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; @@ -16,6 +21,8 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.web.server.ResponseStatusException; +import java.time.Instant; +import java.time.temporal.ChronoUnit; import java.util.List; import java.util.Optional; @@ -30,6 +37,10 @@ public class SponsorService { SponsorRepository sponsorRepository; UserInfoRepository userInfoRepository; ValidateSponsorForCompliance validateSponsorForCompliance; + DeletedUserRepository deletedUserRepository; + ServerInfoConfig serverInfoConfig; + EmailService emailService; + EmailDefaultProps emailDefaultProps; @Transactional(readOnly = true) public Page getAllSponsors(Integer page, Integer size) { @@ -75,18 +86,35 @@ public void deleteSponsor(long id, Authentication authentication) { Optional sponsor = sponsorRepository.findById(id); validateSponsorForCompliance.userVerification(sponsor, user, id); - Sponsor deletableSponsor = sponsor.get(); - List kudosList = deletableSponsor.getKudoses().stream().map(i -> { - i.setSponsor(null); - return i; - }).toList(); - deletableSponsor.setKudoses(kudosList); - userInfoRepository.delete(user.get()); + UserInfo userObject = user.get(); + userObject.setIsLocked(true); + + DeletedUser deletedUser = DeletedUser.builder() + .deletedUser(userObject) + .timeToDelete(Instant.now().plus(3, ChronoUnit.DAYS)) + .build(); + + deletedUserRepository.save(deletedUser); + userInfoRepository.save(userObject); + + String userActivateAccountLink = serverInfoConfig.getFullServerAddress() + + "/api/activate?uuid=" + userObject.getUuid(); + + emailService.sendEmail(userObject.getLogin(), + emailDefaultProps.userDeletedSubject(), + emailDefaultProps.userDeleted().formatted(userActivateAccountLink)); +// Sponsor deletableSponsor = sponsor.get(); +// List kudosList = deletableSponsor.getKudoses().stream().map(i -> { +// i.setSponsor(null); +// return i; +// }).toList(); +// deletableSponsor.setKudoses(kudosList); +// userInfoRepository.delete(userObject); } private void checkEditSponsorNull(EditSponsor editSponsor) { if (editSponsor.firstName() == null && editSponsor.lastName() == null && editSponsor.image() == null && - editSponsor.countOfKudos() == null) + editSponsor.countOfKudos() == null) throw new ResponseStatusException(FORBIDDEN, "you did not provide information to make changes"); } } \ No newline at end of file From d547e6def97a089f53abbaf1f9b29e2024678e27 Mon Sep 17 00:00:00 2001 From: Denis Boyko Date: Sun, 21 May 2023 19:38:03 +0300 Subject: [PATCH 12/19] add DeleteUsersScheduler --- .../user/util/DeleteUsersScheduler.java | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 src/main/java/com/provedcode/user/util/DeleteUsersScheduler.java diff --git a/src/main/java/com/provedcode/user/util/DeleteUsersScheduler.java b/src/main/java/com/provedcode/user/util/DeleteUsersScheduler.java new file mode 100644 index 0000000..30d182f --- /dev/null +++ b/src/main/java/com/provedcode/user/util/DeleteUsersScheduler.java @@ -0,0 +1,29 @@ +package com.provedcode.user.util; + +import com.provedcode.user.repo.DeletedUserRepository; +import com.provedcode.user.repo.UserInfoRepository; +import lombok.AllArgsConstructor; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import java.time.Instant; + +@Component +@EnableScheduling +@AllArgsConstructor +public class DeleteUsersScheduler { + UserInfoRepository userInfoRepository; + DeletedUserRepository deletedUsersRepository; + + @Scheduled(fixedRate = 180000) // every 3 min + public void runTask() { + deletedUsersRepository.findAll() + .forEach(user -> { + if (user.getTimeToDelete().isAfter(Instant.now())) { + deletedUsersRepository.delete(user); + userInfoRepository.deleteById(user.getDeletedUser().getId()); + } + }); + } +} From 2e880c9d07c416d409c553dd3b40630514d9e242 Mon Sep 17 00:00:00 2001 From: Denis Boyko Date: Sun, 21 May 2023 19:38:58 +0300 Subject: [PATCH 13/19] update properties --- src/main/resources/application.properties | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index b1c9f41..d3d2982 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -42,10 +42,8 @@ spring.liquibase.enabled=true ## spring.mail.host=smtp.gmail.com spring.mail.port=587 -#spring.mail.username=${EMAIL_USER} -#spring.mail.password=${EMAIL_PASSWORD} -spring.mail.username=denisboyko702@gmail.com -spring.mail.password=bhbsggqqtpvvnipp +spring.mail.username=${EMAIL_USER} +spring.mail.password=${EMAIL_PASSWORD} spring.mail.properties.mail.smtp.auth=true spring.mail.properties.mail.smtp.starttls.enable=true # Default email response From e4a3980a53a8472f0f6b0e37140a7b8f550507ce Mon Sep 17 00:00:00 2001 From: Maslyna Date: Sun, 21 May 2023 21:32:47 +0200 Subject: [PATCH 14/19] AWS minore update --- .../provedcode/aws/controller/AWSS3BucketController.java | 7 ++++--- src/main/java/com/provedcode/aws/service/FileService.java | 2 +- src/main/java/com/provedcode/aws/service/S3Service.java | 8 +++++++- .../com/provedcode/kudos/controller/KudosController.java | 1 - 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/provedcode/aws/controller/AWSS3BucketController.java b/src/main/java/com/provedcode/aws/controller/AWSS3BucketController.java index 926e991..54bd25f 100644 --- a/src/main/java/com/provedcode/aws/controller/AWSS3BucketController.java +++ b/src/main/java/com/provedcode/aws/controller/AWSS3BucketController.java @@ -21,10 +21,11 @@ public class AWSS3BucketController { @PostSetNewUserImageApiDoc @PreAuthorize("hasRole('TALENT')") - @PostMapping("/image/upload") + @PostMapping("/talents/{talent-id}/image/upload") public void setNewUserImage(@RequestParam("file") MultipartFile file, - Authentication authentication) { - fileService.setNewUserImage(file, authentication); + @PathVariable("talent-id") Long talentId, + Authentication authentication) { + fileService.setNewUserImage(file, talentId, authentication); } @GetAllAWSBucketFilesDevApiDoc diff --git a/src/main/java/com/provedcode/aws/service/FileService.java b/src/main/java/com/provedcode/aws/service/FileService.java index 1c1d9e6..bb771c2 100644 --- a/src/main/java/com/provedcode/aws/service/FileService.java +++ b/src/main/java/com/provedcode/aws/service/FileService.java @@ -16,7 +16,7 @@ public interface FileService { List listAllFiles(); - void setNewUserImage(MultipartFile file, Authentication authentication); + void setNewUserImage(MultipartFile file, Long talentId, Authentication authentication); URL generetePresingedUrlFor7Days(String fileFullPath); } diff --git a/src/main/java/com/provedcode/aws/service/S3Service.java b/src/main/java/com/provedcode/aws/service/S3Service.java index a7a4dc9..5c753ef 100644 --- a/src/main/java/com/provedcode/aws/service/S3Service.java +++ b/src/main/java/com/provedcode/aws/service/S3Service.java @@ -6,6 +6,7 @@ import com.amazonaws.services.s3.model.*; import com.amazonaws.util.IOUtils; import com.provedcode.config.AWSProperties; +import com.provedcode.talent.model.entity.Talent; import com.provedcode.talent.repo.TalentRepository; import com.provedcode.user.model.entity.UserInfo; import com.provedcode.user.repo.UserInfoRepository; @@ -75,7 +76,7 @@ public List listAllFiles() { } @Override - public void setNewUserImage(MultipartFile file, Authentication authentication) { + public void setNewUserImage(MultipartFile file, Long talentId, Authentication authentication) { if (file.isEmpty()) { throw new ResponseStatusException(BAD_REQUEST, "file must be not empty, actual file-size: %s".formatted(file.getSize())); } @@ -84,6 +85,11 @@ public void setNewUserImage(MultipartFile file, Authentication authentication) { } UserInfo user = userInfoRepository.findByLogin(authentication.getName()) .orElseThrow(() -> new ResponseStatusException(NOT_FOUND, "user with login = {%s} not found".formatted(authentication.getName()))); + Talent talent = talentRepository.findById(talentId) + .orElseThrow(() -> new ResponseStatusException(NOT_FOUND, "talent with id = {%s} not found".formatted(talentId))); + if (!user.getTalent().equals(talent)) { + throw new ResponseStatusException(FORBIDDEN, "You cannot change another talent"); + } try { String fileType = getFileType(file); diff --git a/src/main/java/com/provedcode/kudos/controller/KudosController.java b/src/main/java/com/provedcode/kudos/controller/KudosController.java index 878474b..3b46751 100644 --- a/src/main/java/com/provedcode/kudos/controller/KudosController.java +++ b/src/main/java/com/provedcode/kudos/controller/KudosController.java @@ -14,7 +14,6 @@ import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; -import java.util.Optional; @RestController @AllArgsConstructor From 92a4f74b5a2463959b118e4bca6a53e4f7b605f1 Mon Sep 17 00:00:00 2001 From: Maslyna Date: Sun, 21 May 2023 21:48:35 +0200 Subject: [PATCH 15/19] update --- .../sponsor/controller/SponsorController.java | 16 +++++-- .../sponsor/service/SponsorService.java | 47 ++++++++++++------- .../user/model/entity/DeletedUser.java | 3 +- .../user/model/entity/UserInfo.java | 1 - .../user/repo/DeletedUserRepository.java | 3 ++ .../impl/AuthenticationServiceImpl.java | 9 ++-- .../changeset/V5/deleted-user-schema.sql | 9 ++-- .../changeset/V5/user-info-schema-V5.sql | 1 - 8 files changed, 54 insertions(+), 35 deletions(-) diff --git a/src/main/java/com/provedcode/sponsor/controller/SponsorController.java b/src/main/java/com/provedcode/sponsor/controller/SponsorController.java index 39c5bad..09aa4d0 100644 --- a/src/main/java/com/provedcode/sponsor/controller/SponsorController.java +++ b/src/main/java/com/provedcode/sponsor/controller/SponsorController.java @@ -23,13 +23,13 @@ @RestController @AllArgsConstructor @Validated -@RequestMapping("/api/v3") +@RequestMapping("/api") public class SponsorController { SponsorService sponsorService; SponsorMapper sponsorMapper; @GetAllSponsorsApiDoc - @GetMapping("/sponsors") + @GetMapping("/v3/sponsors") Page getSponsors(@RequestParam(value = "page", defaultValue = "0") @PositiveOrZero Integer page, @RequestParam(value = "size", defaultValue = "5") @Min(1) @Max(1000) Integer size) { return sponsorService.getAllSponsors(page, size).map(sponsorMapper::toDto); @@ -37,14 +37,14 @@ Page getSponsors(@RequestParam(value = "page", defaultValue = "0") @ @GetSponsorApiDoc @PreAuthorize("hasRole('SPONSOR')") - @GetMapping("/sponsors/{id}") + @GetMapping("/v3/sponsors/{id}") SponsorDTO getSponsor(@PathVariable("id") long id, Authentication authentication) { return sponsorMapper.toDto(sponsorService.getSponsorById(id, authentication)); } @PatchEditSponsorApiDoc @PreAuthorize("hasRole('SPONSOR')") - @PatchMapping("/sponsors/{id}") + @PatchMapping("/v3/sponsors/{id}") SponsorDTO editSponsor(@PathVariable("id") long id, @RequestBody EditSponsor editSponsor, Authentication authentication) { @@ -53,8 +53,14 @@ SponsorDTO editSponsor(@PathVariable("id") long id, @DeleteSponsorApiDoc @PreAuthorize("hasRole('SPONSOR')") - @DeleteMapping("/sponsors/{id}") + @DeleteMapping("/v3/sponsors/{id}") void deleteSponsor(@PathVariable("id") long id, Authentication authentication) { sponsorService.deleteSponsor(id, authentication); } + + @PreAuthorize("hasRole('SPONSOR')") + @DeleteMapping("/v5/sponsors/{sponsor-id}") + void deactivateSponsor(@PathVariable("sponsor-id") long sponsorId, Authentication authentication) { + sponsorService.deactivateSponsor(sponsorId, authentication); + } } \ No newline at end of file diff --git a/src/main/java/com/provedcode/sponsor/service/SponsorService.java b/src/main/java/com/provedcode/sponsor/service/SponsorService.java index efcec1d..6646633 100644 --- a/src/main/java/com/provedcode/sponsor/service/SponsorService.java +++ b/src/main/java/com/provedcode/sponsor/service/SponsorService.java @@ -26,8 +26,7 @@ import java.util.List; import java.util.Optional; -import static org.springframework.http.HttpStatus.BAD_REQUEST; -import static org.springframework.http.HttpStatus.FORBIDDEN; +import static org.springframework.http.HttpStatus.*; @Service @AllArgsConstructor @@ -86,8 +85,35 @@ public void deleteSponsor(long id, Authentication authentication) { Optional sponsor = sponsorRepository.findById(id); validateSponsorForCompliance.userVerification(sponsor, user, id); - UserInfo userObject = user.get(); - userObject.setIsLocked(true); + Sponsor deletableSponsor = sponsor.get(); + List kudosList = deletableSponsor.getKudoses().stream().map(i -> { + i.setSponsor(null); + return i; + }).toList(); + deletableSponsor.setKudoses(kudosList); + userInfoRepository.delete(user.get()); + } + + private void checkEditSponsorNull(EditSponsor editSponsor) { + if (editSponsor.firstName() == null && editSponsor.lastName() == null && editSponsor.image() == null && + editSponsor.countOfKudos() == null) + throw new ResponseStatusException(FORBIDDEN, "you did not provide information to make changes"); + } + + public void deactivateSponsor(long sponsorId, Authentication authentication) { + UserInfo user = userInfoRepository.findByLogin(authentication.getName()) + .orElseThrow(() -> new ResponseStatusException(NOT_FOUND, + "user with login = %s not found".formatted(authentication.getName()))); + + Sponsor sponsor = sponsorRepository.findById(sponsorId) + .orElseThrow(() -> new ResponseStatusException(NOT_FOUND, + "sponsor with id = %s not found".formatted(sponsorId))); + + if (!sponsor.equals(user.getSponsor())) { + throw new ResponseStatusException(FORBIDDEN, "you cannot update/delete another sponsor"); + } + + user.setIsLocked(true); DeletedUser deletedUser = DeletedUser.builder() .deletedUser(userObject) @@ -103,18 +129,5 @@ public void deleteSponsor(long id, Authentication authentication) { emailService.sendEmail(userObject.getLogin(), emailDefaultProps.userDeletedSubject(), emailDefaultProps.userDeleted().formatted(userActivateAccountLink)); -// Sponsor deletableSponsor = sponsor.get(); -// List kudosList = deletableSponsor.getKudoses().stream().map(i -> { -// i.setSponsor(null); -// return i; -// }).toList(); -// deletableSponsor.setKudoses(kudosList); -// userInfoRepository.delete(userObject); - } - - private void checkEditSponsorNull(EditSponsor editSponsor) { - if (editSponsor.firstName() == null && editSponsor.lastName() == null && editSponsor.image() == null && - editSponsor.countOfKudos() == null) - throw new ResponseStatusException(FORBIDDEN, "you did not provide information to make changes"); } } \ No newline at end of file diff --git a/src/main/java/com/provedcode/user/model/entity/DeletedUser.java b/src/main/java/com/provedcode/user/model/entity/DeletedUser.java index f651356..6551f44 100644 --- a/src/main/java/com/provedcode/user/model/entity/DeletedUser.java +++ b/src/main/java/com/provedcode/user/model/entity/DeletedUser.java @@ -1,7 +1,5 @@ package com.provedcode.user.model.entity; -import com.provedcode.sponsor.model.entity.Sponsor; -import com.provedcode.talent.model.entity.Talent; import jakarta.persistence.*; import lombok.*; import lombok.extern.slf4j.Slf4j; @@ -23,4 +21,5 @@ public class DeletedUser { @OneToOne @JoinColumn(name = "id", referencedColumnName = "id") private UserInfo deletedUser; + private String uuidForActivate; } \ No newline at end of file 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 33ee020..81db861 100644 --- a/src/main/java/com/provedcode/user/model/entity/UserInfo.java +++ b/src/main/java/com/provedcode/user/model/entity/UserInfo.java @@ -55,6 +55,5 @@ public class UserInfo { @ManyToMany(fetch = FetchType.EAGER) @JoinTable(name = "users_authorities", joinColumns = @JoinColumn(name = "user_id"), inverseJoinColumns = @JoinColumn(name = "authority_id")) private Set authorities = new LinkedHashSet<>(); - private String uuid; private Boolean isLocked; } \ No newline at end of file diff --git a/src/main/java/com/provedcode/user/repo/DeletedUserRepository.java b/src/main/java/com/provedcode/user/repo/DeletedUserRepository.java index 666a9a4..dbeead1 100644 --- a/src/main/java/com/provedcode/user/repo/DeletedUserRepository.java +++ b/src/main/java/com/provedcode/user/repo/DeletedUserRepository.java @@ -4,7 +4,10 @@ import com.provedcode.user.model.entity.UserInfo; import org.springframework.data.jpa.repository.JpaRepository; +import java.util.Optional; + public interface DeletedUserRepository extends JpaRepository { + Optional findByUuidForActivate(String uuidForActivate); long deleteByDeletedUser(UserInfo deletedUser); } \ No newline at end of file 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 cab4ce1..572456a 100644 --- a/src/main/java/com/provedcode/user/service/impl/AuthenticationServiceImpl.java +++ b/src/main/java/com/provedcode/user/service/impl/AuthenticationServiceImpl.java @@ -9,6 +9,7 @@ import com.provedcode.user.model.dto.TalentRegistrationDTO; import com.provedcode.user.model.dto.UserInfoDTO; import com.provedcode.user.model.entity.Authority; +import com.provedcode.user.model.entity.DeletedUser; import com.provedcode.user.model.entity.UserInfo; import com.provedcode.user.repo.AuthorityRepository; import com.provedcode.user.repo.DeletedUserRepository; @@ -16,7 +17,6 @@ import com.provedcode.user.service.AuthenticationService; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.apache.catalina.User; import org.springframework.http.HttpStatus; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.crypto.password.PasswordEncoder; @@ -83,7 +83,6 @@ public UserInfoDTO register(TalentRegistrationDTO user) { .login(user.login()) .password(passwordEncoder.encode(user.password())) .authorities(Set.of(authorityRepository.findByAuthority(Role.TALENT).orElseThrow())) - .uuid(UUID.randomUUID().toString()) .isLocked(false) .build(); userInfoRepository.save(userInfo); @@ -116,7 +115,6 @@ public UserInfoDTO register(SponsorRegistrationDTO user) { .password(passwordEncoder.encode(user.password())) .authorities( Set.of(authorityRepository.findByAuthority(Role.SPONSOR).orElseThrow())) - .uuid(UUID.randomUUID().toString()) .isLocked(false) .build(); userInfoRepository.save(userInfo); @@ -149,10 +147,11 @@ private Jwt generateJWTToken(Long id, String login, Collection new ResponseStatusException(NOT_FOUND)); + UserInfo user = deletedUser.getDeletedUser(); user.setIsLocked(false); - deletedUserRepository.deleteByDeletedUser(user); + deletedUserRepository.deleteById(deletedUser.getId()); userInfoRepository.save(user); } } \ No newline at end of file diff --git a/src/main/resources/db/changelog/changeset/V5/deleted-user-schema.sql b/src/main/resources/db/changelog/changeset/V5/deleted-user-schema.sql index a5b0514..012c6ef 100644 --- a/src/main/resources/db/changelog/changeset/V5/deleted-user-schema.sql +++ b/src/main/resources/db/changelog/changeset/V5/deleted-user-schema.sql @@ -1,9 +1,10 @@ CREATE TABLE deleted_user ( - id BIGINT GENERATED BY DEFAULT AS IDENTITY NOT NULL, - time_to_delete TIMESTAMP WITHOUT TIME ZONE, - CONSTRAINT pk_deleteduser PRIMARY KEY (id) + id BIGINT GENERATED BY DEFAULT AS IDENTITY NOT NULL, + time_to_delete TIMESTAMP WITHOUT TIME ZONE, + uuid_for_activate VARCHAR(255), + CONSTRAINT pk_deleted_user PRIMARY KEY (id) ); ALTER TABLE deleted_user - ADD CONSTRAINT FK_DELETEDUSER_ON_ID FOREIGN KEY (id) REFERENCES users_info (id); \ No newline at end of file + ADD CONSTRAINT FK_DELETED_USER_ON_ID FOREIGN KEY (id) REFERENCES users_info (id); \ No newline at end of file diff --git a/src/main/resources/db/changelog/changeset/V5/user-info-schema-V5.sql b/src/main/resources/db/changelog/changeset/V5/user-info-schema-V5.sql index 00d7ef5..3028fe7 100644 --- a/src/main/resources/db/changelog/changeset/V5/user-info-schema-V5.sql +++ b/src/main/resources/db/changelog/changeset/V5/user-info-schema-V5.sql @@ -8,7 +8,6 @@ CREATE TABLE users_info ( sponsor_id BIGINT REFERENCES sponsors, login VARCHAR(100) NOT NULL, password VARCHAR(255) NOT NULL, - uuid VARCHAR(255), is_locked BOOLEAN, PRIMARY KEY (id) ); From 56baa86743f9cccfd8cf6bff89885fd6e8d59e59 Mon Sep 17 00:00:00 2001 From: Maslyna Date: Mon, 22 May 2023 19:26:58 +0200 Subject: [PATCH 16/19] sponsor service update --- .../sponsor/service/SponsorService.java | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/src/main/java/com/provedcode/sponsor/service/SponsorService.java b/src/main/java/com/provedcode/sponsor/service/SponsorService.java index 6646633..22f1689 100644 --- a/src/main/java/com/provedcode/sponsor/service/SponsorService.java +++ b/src/main/java/com/provedcode/sponsor/service/SponsorService.java @@ -13,6 +13,7 @@ import com.provedcode.user.repo.DeletedUserRepository; import com.provedcode.user.repo.UserInfoRepository; import com.provedcode.user.service.impl.EmailService; +import jakarta.validation.constraints.NotNull; import lombok.AllArgsConstructor; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; @@ -25,6 +26,7 @@ import java.time.temporal.ChronoUnit; import java.util.List; import java.util.Optional; +import java.util.UUID; import static org.springframework.http.HttpStatus.*; @@ -86,12 +88,16 @@ public void deleteSponsor(long id, Authentication authentication) { validateSponsorForCompliance.userVerification(sponsor, user, id); Sponsor deletableSponsor = sponsor.get(); + deleteSponsorByUser(user.get(), deletableSponsor); + } + + private void deleteSponsorByUser(UserInfo user, @NotNull Sponsor deletableSponsor) { List kudosList = deletableSponsor.getKudoses().stream().map(i -> { i.setSponsor(null); return i; }).toList(); deletableSponsor.setKudoses(kudosList); - userInfoRepository.delete(user.get()); + userInfoRepository.delete(user); } private void checkEditSponsorNull(EditSponsor editSponsor) { @@ -104,29 +110,27 @@ public void deactivateSponsor(long sponsorId, Authentication authentication) { UserInfo user = userInfoRepository.findByLogin(authentication.getName()) .orElseThrow(() -> new ResponseStatusException(NOT_FOUND, "user with login = %s not found".formatted(authentication.getName()))); - Sponsor sponsor = sponsorRepository.findById(sponsorId) .orElseThrow(() -> new ResponseStatusException(NOT_FOUND, "sponsor with id = %s not found".formatted(sponsorId))); - if (!sponsor.equals(user.getSponsor())) { throw new ResponseStatusException(FORBIDDEN, "you cannot update/delete another sponsor"); } - user.setIsLocked(true); DeletedUser deletedUser = DeletedUser.builder() - .deletedUser(userObject) + .deletedUser(user) .timeToDelete(Instant.now().plus(3, ChronoUnit.DAYS)) + .uuidForActivate(UUID.randomUUID().toString()) .build(); - deletedUserRepository.save(deletedUser); - userInfoRepository.save(userObject); - String userActivateAccountLink = serverInfoConfig.getFullServerAddress() + - "/api/activate?uuid=" + userObject.getUuid(); + "/api/v5/activate?uuid=" + deletedUser.getUuidForActivate(); + + deletedUserRepository.save(deletedUser); + userInfoRepository.save(user); - emailService.sendEmail(userObject.getLogin(), + emailService.sendEmail(user.getLogin(), emailDefaultProps.userDeletedSubject(), emailDefaultProps.userDeleted().formatted(userActivateAccountLink)); } From a3fcd782746fb990402be53f635c718d58137048 Mon Sep 17 00:00:00 2001 From: Maslyna Date: Tue, 23 May 2023 02:39:15 +0200 Subject: [PATCH 17/19] Requested update: sponsor can see amount of kudos on user account User restore account now available! --- .../com/provedcode/config/SecurityConfig.java | 2 +- .../kudos/controller/KudosController.java | 2 +- .../kudos/service/KudosService.java | 42 ++++++++++-- .../sponsor/service/SponsorService.java | 2 +- .../talent/controller/TalentController.java | 7 ++ .../talent/service/TalentService.java | 18 +++++- .../controller/AuthenticationController.java | 2 +- .../user/model/entity/DeletedUser.java | 2 +- .../user/model/entity/UserInfo.java | 2 + .../user/repo/DeletedUserRepository.java | 4 +- .../user/repo/UserInfoRepository.java | 1 - .../impl/AuthenticationServiceImpl.java | 19 +++--- .../user/util/DeleteUsersScheduler.java | 29 --------- .../user/util/UsersSchedulerService.java | 64 +++++++++++++++++++ .../changeset/V5/deleted-user-schema.sql | 8 ++- .../db/changelog/db.changelog-master.yaml | 2 +- 16 files changed, 148 insertions(+), 58 deletions(-) delete mode 100644 src/main/java/com/provedcode/user/util/DeleteUsersScheduler.java create mode 100644 src/main/java/com/provedcode/user/util/UsersSchedulerService.java diff --git a/src/main/java/com/provedcode/config/SecurityConfig.java b/src/main/java/com/provedcode/config/SecurityConfig.java index c2ee16f..6856082 100644 --- a/src/main/java/com/provedcode/config/SecurityConfig.java +++ b/src/main/java/com/provedcode/config/SecurityConfig.java @@ -59,7 +59,7 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti .requestMatchers(antMatcher("/v3/api-docs/**")).permitAll() // for openAPI .requestMatchers(antMatcher("/swagger-ui/**")).permitAll() // for openAPI .requestMatchers(antMatcher("/swagger-ui.html")).permitAll() // for openAPI - .requestMatchers(antMatcher(HttpMethod.GET, "/api/activate")).permitAll()// for email account recovery + .requestMatchers(antMatcher(HttpMethod.GET, "/api/v5/activate")).permitAll()// for email account recovery .anyRequest().authenticated() ); diff --git a/src/main/java/com/provedcode/kudos/controller/KudosController.java b/src/main/java/com/provedcode/kudos/controller/KudosController.java index 3b46751..5af76e9 100644 --- a/src/main/java/com/provedcode/kudos/controller/KudosController.java +++ b/src/main/java/com/provedcode/kudos/controller/KudosController.java @@ -30,7 +30,7 @@ KudosAmount getKudosForSponsor(@PathVariable("sponsor-id") long sponsorId, Authe } @GetAmountOfKudosApiDoc - @PreAuthorize("hasRole('TALENT')") + @PreAuthorize("hasAnyRole('TALENT', 'SPONSOR')") @GetMapping("/proofs/{proof-id}/kudos") KudosAmountWithSponsor getProofKudos(@PathVariable("proof-id") long proofId, Authentication authentication) { return kudosService.getProofKudos(proofId, authentication); diff --git a/src/main/java/com/provedcode/kudos/service/KudosService.java b/src/main/java/com/provedcode/kudos/service/KudosService.java index 595396e..4c175a4 100644 --- a/src/main/java/com/provedcode/kudos/service/KudosService.java +++ b/src/main/java/com/provedcode/kudos/service/KudosService.java @@ -1,6 +1,5 @@ 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; @@ -67,9 +66,6 @@ public KudosAmountWithSponsor getProofKudos(long proofId, Authentication authent 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))); @@ -77,10 +73,44 @@ public KudosAmountWithSponsor getProofKudos(long proofId, Authentication authent Long countOfAllKudos = talentProof.getProofSkills() .stream().flatMap(proofSkills -> proofSkills.getKudos() .stream().map(Kudos::getAmount)) - .reduce(0L, (prev, next) -> prev + next); + .reduce(0L, Long::sum); + Map> skillsMap = new HashMap<>(); + + if (userInfo.getSponsor() != null && userInfo.getTalent() == null) { + Sponsor sponsor = sponsorRepository.findById(userInfo.getSponsor().getId()) + .orElseThrow(() -> new ResponseStatusException(NOT_FOUND, + "User with login = %s not found".formatted( + login))); + talentProof.getProofSkills().forEach(proofSkill -> { + String skill = proofSkill.getSkill().getSkill(); + Map kudosFromSponsor = talentProof.getProofSkills().stream() + .map(proofSkills -> { + proofSkills.setKudos(proofSkills.getKudos().stream() + .filter(kudos -> sponsor.equals(kudos.getSponsor())).toList()); + return proofSkills; + }) + .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(); + } + + Talent talent = talentRepository.findById(userInfo.getTalent().getId()) + .orElseThrow(() -> new ResponseStatusException(NOT_FOUND, + "Talent with login = %s not found".formatted(login))); 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() diff --git a/src/main/java/com/provedcode/sponsor/service/SponsorService.java b/src/main/java/com/provedcode/sponsor/service/SponsorService.java index 22f1689..40bff99 100644 --- a/src/main/java/com/provedcode/sponsor/service/SponsorService.java +++ b/src/main/java/com/provedcode/sponsor/service/SponsorService.java @@ -13,6 +13,7 @@ import com.provedcode.user.repo.DeletedUserRepository; import com.provedcode.user.repo.UserInfoRepository; import com.provedcode.user.service.impl.EmailService; +import com.provedcode.user.util.UsersSchedulerService; import jakarta.validation.constraints.NotNull; import lombok.AllArgsConstructor; import org.springframework.data.domain.Page; @@ -128,7 +129,6 @@ public void deactivateSponsor(long sponsorId, Authentication authentication) { "/api/v5/activate?uuid=" + deletedUser.getUuidForActivate(); deletedUserRepository.save(deletedUser); - userInfoRepository.save(user); emailService.sendEmail(user.getLogin(), emailDefaultProps.userDeletedSubject(), diff --git a/src/main/java/com/provedcode/talent/controller/TalentController.java b/src/main/java/com/provedcode/talent/controller/TalentController.java index 3c0b319..157cc17 100644 --- a/src/main/java/com/provedcode/talent/controller/TalentController.java +++ b/src/main/java/com/provedcode/talent/controller/TalentController.java @@ -97,4 +97,11 @@ StatisticsDTO getStatisticsForTalent(@PathVariable("talent-id") long talentId, Authentication authentication) { return talentService.getStatisticsForTalent(talentId, authentication); } + + @PreAuthorize("hasRole('TALENT')") + @DeleteMapping("v5/talents/{talent-id}") + void deactivateTalentById(@PathVariable("talent-id") long talentId, + Authentication authentication) { + talentService.deactivateTalentById(talentId, authentication); + } } \ No newline at end of file diff --git a/src/main/java/com/provedcode/talent/service/TalentService.java b/src/main/java/com/provedcode/talent/service/TalentService.java index eb4618d..e0e8e54 100644 --- a/src/main/java/com/provedcode/talent/service/TalentService.java +++ b/src/main/java/com/provedcode/talent/service/TalentService.java @@ -152,21 +152,33 @@ public SessionInfoDTO deleteTalentById(long id, Authentication authentication) { validateTalentForCompliance.userVerification(talent, userInfo, id); + UserInfo user = userInfo.get(); + userInfoRepository.delete(user); + + return new SessionInfoDTO("deleted", "null"); + } + + public void deactivateTalentById(long id, Authentication authentication) { + Optional talent = talentRepository.findById(id); + Optional userInfo = userInfoRepository.findByLogin(authentication.getName()); + + validateTalentForCompliance.userVerification(talent, userInfo, id); + UserInfo user = userInfo.get(); user.setIsLocked(true); DeletedUser deletedUser = DeletedUser.builder() .deletedUser(user) .timeToDelete(Instant.now().plus(3, ChronoUnit.DAYS)) + .uuidForActivate(UUID.randomUUID().toString()) .build(); deletedUserRepository.save(deletedUser); userInfoRepository.save(user); - String userActivateAccountLink = serverInfoConfig.getFullServerAddress() + "/api/activate?uuid=" + user.getUuid(); + String userActivateAccountLink = serverInfoConfig.getFullServerAddress() + "/api/v5/activate?uuid=" + deletedUser.getUuidForActivate(); + emailService.sendEmail(user.getLogin(), emailDefaultProps.userDeletedSubject(), emailDefaultProps.userDeleted().formatted(userActivateAccountLink)); - - return new SessionInfoDTO("deleted", "null"); } private void checkEditTalentNull(EditTalent editTalent) { diff --git a/src/main/java/com/provedcode/user/controller/AuthenticationController.java b/src/main/java/com/provedcode/user/controller/AuthenticationController.java index 9377cf6..e25f35b 100644 --- a/src/main/java/com/provedcode/user/controller/AuthenticationController.java +++ b/src/main/java/com/provedcode/user/controller/AuthenticationController.java @@ -42,7 +42,7 @@ UserInfoDTO register(@RequestBody @Valid SponsorRegistrationDTO user) { return authenticationService.register(user); } - @GetMapping("/activate") + @GetMapping("/v5/activate") void activateAccount(@RequestParam("uuid") String uuid) { authenticationService.activateAccount(uuid); } diff --git a/src/main/java/com/provedcode/user/model/entity/DeletedUser.java b/src/main/java/com/provedcode/user/model/entity/DeletedUser.java index 6551f44..105f920 100644 --- a/src/main/java/com/provedcode/user/model/entity/DeletedUser.java +++ b/src/main/java/com/provedcode/user/model/entity/DeletedUser.java @@ -19,7 +19,7 @@ public class DeletedUser { private Long id; private Instant timeToDelete; @OneToOne - @JoinColumn(name = "id", referencedColumnName = "id") + @JoinColumn(name = "user_id", referencedColumnName = "id") private UserInfo deletedUser; private String uuidForActivate; } \ No newline at end of file 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 81db861..7250e83 100644 --- a/src/main/java/com/provedcode/user/model/entity/UserInfo.java +++ b/src/main/java/com/provedcode/user/model/entity/UserInfo.java @@ -55,5 +55,7 @@ public class UserInfo { @ManyToMany(fetch = FetchType.EAGER) @JoinTable(name = "users_authorities", joinColumns = @JoinColumn(name = "user_id"), inverseJoinColumns = @JoinColumn(name = "authority_id")) private Set authorities = new LinkedHashSet<>(); + + @Column(columnDefinition = "FALSE") private Boolean isLocked; } \ No newline at end of file diff --git a/src/main/java/com/provedcode/user/repo/DeletedUserRepository.java b/src/main/java/com/provedcode/user/repo/DeletedUserRepository.java index dbeead1..de3478a 100644 --- a/src/main/java/com/provedcode/user/repo/DeletedUserRepository.java +++ b/src/main/java/com/provedcode/user/repo/DeletedUserRepository.java @@ -3,11 +3,13 @@ import com.provedcode.user.model.entity.DeletedUser; import com.provedcode.user.model.entity.UserInfo; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; import java.util.Optional; public interface DeletedUserRepository extends JpaRepository { + @Query("select d from DeletedUser d where d.uuidForActivate = ?1") + Optional findByUUID(String uuidForActivate); Optional findByUuidForActivate(String uuidForActivate); - long deleteByDeletedUser(UserInfo deletedUser); } \ No newline at end of file diff --git a/src/main/java/com/provedcode/user/repo/UserInfoRepository.java b/src/main/java/com/provedcode/user/repo/UserInfoRepository.java index 0e97885..0b1e73d 100644 --- a/src/main/java/com/provedcode/user/repo/UserInfoRepository.java +++ b/src/main/java/com/provedcode/user/repo/UserInfoRepository.java @@ -9,6 +9,5 @@ public interface UserInfoRepository extends JpaRepository { long deleteByTalentId(Long talentId); boolean existsByLogin(String login); Optional findByLogin(String login); - Optional findByUuid(String uuid); } \ No newline at end of file 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 572456a..2adc01f 100644 --- a/src/main/java/com/provedcode/user/service/impl/AuthenticationServiceImpl.java +++ b/src/main/java/com/provedcode/user/service/impl/AuthenticationServiceImpl.java @@ -31,7 +31,6 @@ import java.time.Instant; import java.util.Collection; import java.util.Set; -import java.util.UUID; import java.util.function.BiFunction; import java.util.stream.Collectors; @@ -128,6 +127,15 @@ public UserInfoDTO register(SponsorRegistrationDTO user) { return new UserInfoDTO(generateJWTToken(sponsor.getId(), userLogin, userAuthorities).getTokenValue()); } + public void activateAccount(String uuid) { + DeletedUser deletedUser = deletedUserRepository.findByUUID(uuid) + .orElseThrow(() -> new ResponseStatusException(NOT_FOUND)); + UserInfo user = deletedUser.getDeletedUser(); + user.setIsLocked(false); + deletedUserRepository.deleteById(deletedUser.getId()); + userInfoRepository.save(user); + } + private Jwt generateJWTToken(Long id, String login, Collection authorities) { log.info("=== POST /login === auth.login = {}", login); log.info("=== POST /login === auth = {}", authorities); @@ -145,13 +153,4 @@ private Jwt generateJWTToken(Long id, String login, Collection new ResponseStatusException(NOT_FOUND)); - UserInfo user = deletedUser.getDeletedUser(); - user.setIsLocked(false); - deletedUserRepository.deleteById(deletedUser.getId()); - userInfoRepository.save(user); - } } \ No newline at end of file diff --git a/src/main/java/com/provedcode/user/util/DeleteUsersScheduler.java b/src/main/java/com/provedcode/user/util/DeleteUsersScheduler.java deleted file mode 100644 index 30d182f..0000000 --- a/src/main/java/com/provedcode/user/util/DeleteUsersScheduler.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.provedcode.user.util; - -import com.provedcode.user.repo.DeletedUserRepository; -import com.provedcode.user.repo.UserInfoRepository; -import lombok.AllArgsConstructor; -import org.springframework.scheduling.annotation.EnableScheduling; -import org.springframework.scheduling.annotation.Scheduled; -import org.springframework.stereotype.Component; - -import java.time.Instant; - -@Component -@EnableScheduling -@AllArgsConstructor -public class DeleteUsersScheduler { - UserInfoRepository userInfoRepository; - DeletedUserRepository deletedUsersRepository; - - @Scheduled(fixedRate = 180000) // every 3 min - public void runTask() { - deletedUsersRepository.findAll() - .forEach(user -> { - if (user.getTimeToDelete().isAfter(Instant.now())) { - deletedUsersRepository.delete(user); - userInfoRepository.deleteById(user.getDeletedUser().getId()); - } - }); - } -} diff --git a/src/main/java/com/provedcode/user/util/UsersSchedulerService.java b/src/main/java/com/provedcode/user/util/UsersSchedulerService.java new file mode 100644 index 0000000..0633376 --- /dev/null +++ b/src/main/java/com/provedcode/user/util/UsersSchedulerService.java @@ -0,0 +1,64 @@ +package com.provedcode.user.util; + +import com.provedcode.user.repo.DeletedUserRepository; +import com.provedcode.user.repo.UserInfoRepository; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.Instant; +import java.util.concurrent.TimeUnit; + +@Service +@EnableScheduling +@AllArgsConstructor +@Transactional +@Slf4j +public class UsersSchedulerService { + UserInfoRepository userInfoRepository; + DeletedUserRepository deletedUsersRepository; + + @Scheduled(timeUnit = TimeUnit.MINUTES, fixedRate = 1) + void deleteUser() { + deletedUsersRepository.findAll() + .forEach(userToDelete -> { + if (userToDelete.getTimeToDelete().isAfter(Instant.now())) { + log.info("deleting user: id = {}", userToDelete.getDeletedUser().getId()); + deletedUsersRepository.delete(userToDelete); + userInfoRepository.delete(userToDelete.getDeletedUser()); + } + }); + } + +// @Async +// public void scheduleDeletingMethod(String uuid) { +// try { +// TimeUnit.MINUTES.sleep(3); +// log.info("scheduleDeletingMethod = {}", uuid); +// deletedUsersRepository.findByUUID(uuid) +// .ifPresent(deleted -> { +// deletedUsersRepository.delete(deleted); +// deleteUser(deleted.getDeletedUser()); +// }); +// } catch (InterruptedException e) { +// Thread.currentThread().interrupt(); +// } +// } +// +// private void deleteUser(UserInfo user) { +// if (user.getSponsor() != null) { +// List kudosList = user.getSponsor().getKudoses().stream().map(i -> { +// i.setSponsor(null); +// return i; +// }).toList(); +// user.getSponsor().setKudoses(kudosList); +// userInfoRepository.delete(user); +// } +// if (user.getTalent() != null) { +// userInfoRepository.delete(user); +// } +// } +} diff --git a/src/main/resources/db/changelog/changeset/V5/deleted-user-schema.sql b/src/main/resources/db/changelog/changeset/V5/deleted-user-schema.sql index 012c6ef..7f19179 100644 --- a/src/main/resources/db/changelog/changeset/V5/deleted-user-schema.sql +++ b/src/main/resources/db/changelog/changeset/V5/deleted-user-schema.sql @@ -1,10 +1,14 @@ +--liquibase formatted sql +--changeset Maslyna:5 + CREATE TABLE deleted_user ( id BIGINT GENERATED BY DEFAULT AS IDENTITY NOT NULL, time_to_delete TIMESTAMP WITHOUT TIME ZONE, + user_id BIGINT, uuid_for_activate VARCHAR(255), - CONSTRAINT pk_deleted_user PRIMARY KEY (id) + CONSTRAINT pk_deleteduser PRIMARY KEY (id) ); ALTER TABLE deleted_user - ADD CONSTRAINT FK_DELETED_USER_ON_ID FOREIGN KEY (id) REFERENCES users_info (id); \ No newline at end of file + ADD CONSTRAINT FK_DELETEDUSER_ON_USER FOREIGN KEY (user_id) REFERENCES users_info (id); \ No newline at end of file diff --git a/src/main/resources/db/changelog/db.changelog-master.yaml b/src/main/resources/db/changelog/db.changelog-master.yaml index 6e366a2..f09e9a9 100644 --- a/src/main/resources/db/changelog/db.changelog-master.yaml +++ b/src/main/resources/db/changelog/db.changelog-master.yaml @@ -28,4 +28,4 @@ databaseChangeLog: - include: file: db/changelog/changeset/V5/dataKudos-V5.sql - include: - file: db/changelog/changeset/V5/deleted-user-schema.sql + file: db/changelog/changeset/V5/deleted-user-schema.sql From 339b160522617cc4442003ea4b32c2ca9dd6e6ca Mon Sep 17 00:00:00 2001 From: Maslyna Date: Wed, 24 May 2023 12:09:10 +0200 Subject: [PATCH 18/19] MERGE UPDATE --- .../user/util/UsersSchedulerService.java | 2 +- .../db/changelog/changeset/V5/data-V5.sql | 28 +-- .../db/changelog/changeset/V5/skills-data.sql | 223 ++++++++++++++++++ .../changeset/V5/talent-schema-V5.sql | 2 +- .../db/changelog/db.changelog-master.yaml | 3 + 5 files changed, 229 insertions(+), 29 deletions(-) create mode 100644 src/main/resources/db/changelog/changeset/V5/skills-data.sql diff --git a/src/main/java/com/provedcode/user/util/UsersSchedulerService.java b/src/main/java/com/provedcode/user/util/UsersSchedulerService.java index 0633376..63285dd 100644 --- a/src/main/java/com/provedcode/user/util/UsersSchedulerService.java +++ b/src/main/java/com/provedcode/user/util/UsersSchedulerService.java @@ -21,7 +21,7 @@ public class UsersSchedulerService { UserInfoRepository userInfoRepository; DeletedUserRepository deletedUsersRepository; - @Scheduled(timeUnit = TimeUnit.MINUTES, fixedRate = 1) + @Scheduled(timeUnit = TimeUnit.HOURS, fixedRate = 1) void deleteUser() { deletedUsersRepository.findAll() .forEach(userToDelete -> { diff --git a/src/main/resources/db/changelog/changeset/V5/data-V5.sql b/src/main/resources/db/changelog/changeset/V5/data-V5.sql index b897501..2d63889 100644 --- a/src/main/resources/db/changelog/changeset/V5/data-V5.sql +++ b/src/main/resources/db/changelog/changeset/V5/data-V5.sql @@ -5,33 +5,7 @@ insert into authorities (id, authority) values (1, 'TALENT'); insert into authorities (id, authority) values (2, 'SPONSOR'); --- Skill -insert into skills (id, skill) -values (1, 'Java Core'); -insert into skills (id, skill) -values (2, 'Spring Core'); -insert into skills (id, skill) -values (3, 'Spring boot'); -insert into skills (id, skill) -values (4, 'H2 Database'); -insert into skills (id, skill) -values (5, 'Spring Security'); -insert into skills (id, skill) -values (6, 'REST API'); -insert into skills (id, skill) -values (7, 'Git'); -insert into skills (id, skill) -values (8, 'Docker'); -insert into skills (id, skill) -values (9, 'Jira'); -insert into skills (id, skill) -values (10, 'JavaScript Core'); -insert into skills (id, skill) -values (11, 'React'); -insert into skills (id, skill) -values (12, 'Node.js'); -insert into skills (id, skill) -values (13, 'Angular'); + -- Talent -- Serhii Soloviov insert into talents (first_name, last_name, specialization, image) diff --git a/src/main/resources/db/changelog/changeset/V5/skills-data.sql b/src/main/resources/db/changelog/changeset/V5/skills-data.sql new file mode 100644 index 0000000..cd7848c --- /dev/null +++ b/src/main/resources/db/changelog/changeset/V5/skills-data.sql @@ -0,0 +1,223 @@ +--liquibase formatted sql +--changeset Maslyna:0 + +-- Skill +insert into skills (id, skill) +values (1, 'Java Core'); +insert into skills (id, skill) +values (2, 'Spring Core'); +insert into skills (id, skill) +values (3, 'Spring boot'); +insert into skills (id, skill) +values (4, 'H2 Database'); +insert into skills (id, skill) +values (5, 'Spring Security'); +insert into skills (id, skill) +values (6, 'REST API'); +insert into skills (id, skill) +values (7, 'Git'); +insert into skills (id, skill) +values (8, 'Docker'); +insert into skills (id, skill) +values (9, 'Jira'); +insert into skills (id, skill) +values (10, 'JavaScript Core'); +insert into skills (id, skill) +values (11, 'React'); +insert into skills (id, skill) +values (12, 'Node.js'); +insert into skills (id, skill) +values (13, 'Angular'); +-- +INSERT INTO skills (id, skill) +VALUES (14, 'Java Development Kit (JDK)'), + (15, 'Servlet API'), + (16, 'JavaServer Pages (JSP)'), + (17, 'JavaServer Faces (JSF)'), + (18, 'Spring Framework'), + (19, 'Hibernate'), + (20, 'Apache Struts'), + (21, 'MySQL'), + (22, 'PostgreSQL'), + (23, 'Oracle'), + (24, 'SQL'), + (25, 'Eclipse'), + (26, 'IntelliJ IDEA'), + (27, 'NetBeans'), + (28, 'Apache Tomcat'), + (29, 'Jetty'), + (30, 'JBoss'), + (31, 'Git'), + (32, 'SVN'), + (33, 'Mercurial'), + (34, 'Algorithms and data structures'), + (35, 'SOAP'), + (36, 'REST'), + (37, 'JSON'), + (38, 'XML'), + (39, 'Multithreading and parallelism'); +INSERT INTO skills (id, skill) +VALUES (40, 'Django'), + (41, 'Flask'), + (42, 'Pyramid'), + (43, 'SQLite'), + (44, 'NoSQL'), + (45, 'MongoDB'), + (46, 'Redis'), + (47, 'BeautifulSoup'), + (48, 'Scrapy'), + (49, 'asyncio'), + (50, 'Twisted'), + (51, 'NumPy'), + (52, 'SciPy'), + (53, 'Pandas'), + (54, 'scikit-learn'), + (55, 'TensorFlow'), + (56, 'PyTorch'), + (57, 'JavaScript'), + (58, 'HTML'), + (59, 'CSS'), + (60, 'DOM'), + (61, 'React'), + (62, 'Angular'), + (63, 'Vue.js'), + (64, 'jQuery'), + (65, 'AJAX'), + (66, 'XSS (Cross-Site Scripting)'), + (67, 'CSRF (Cross-Site Request Forgery)'), + (68, 'Swift'), + (69, 'UIKit'), + (70, 'AppKit'), + (71, 'Core Data'), + (72, 'Core Animation'), + (73, 'Core Location'), + (74, 'Core Graphics'), + (75, 'Storyboards'), + (76, 'Interface Builder'), + (77, 'Auto Layout'), + (78, 'CoreData'), + (79, 'Asynchronous programming'), + (80, 'MVC (Model-View-Controller)'), + (81, 'MVVM (Model-View-ViewModel)'), + (82, 'Clean Architecture'), + (83, 'Kotlin'), + (84, 'Android SDK'), + (85, 'Firebase'), + (86, 'Jenkins'), + (87, 'Docker'), + (88, 'UNIX'), + (89, 'Linux OS'), + (90, 'JPA'), + (91, 'JUnit'), + (92, 'OOP'), + (93, 'PHP'), + (94, 'Microservices'), + (95, 'Azure Service Bus'), + (96, 'Typescript'), + (97, 'WinForms'), + (98, 'Firebase Analytics'), + (99, 'Appsflyer'), + (100, 'Amplitude'); +INSERT INTO skills (id, skill) +VALUES (101, 'Marketing'), + (102, 'Sales'), + (103, 'Content Writing'), + (104, 'Graphic Design'), + (105, 'Video Editing'), + (106, 'Project Management'), + (107, 'Leadership'), + (108, 'Negotiation'), + (109, 'Public Speaking'), + (110, 'Time Management'), + (111, 'Critical Thinking'), + (112, 'Problem Solving'), + (113, 'Customer Service'), + (114, 'Data Analysis'), + (115, 'Financial Planning'), + (116, 'Event Planning'), + (117, 'Foreign Languages'), + (118, 'Photography'), + (119, 'Cooking'), + (120, 'Yoga'), + (121, 'Creative Writing'); +INSERT INTO skills (id, skill) +VALUES (122, 'Leadership Development'), + (123, 'Emotional Intelligence'), + (124, 'Teamwork'), + (125, 'Conflict Resolution'), + (126, 'Decision Making'), + (127, 'Presentation Skills'), + (128, 'Networking'), + (129, 'Research Skills'), + (130, 'Problem-solving'), + (131, 'Analytical Thinking'), + (132, 'Creativity'), + (133, 'Attention to Detail'), + (134, 'Organizational Skills'), + (135, 'Time Management'), + (136, 'Adaptability'), + (137, 'Stress Management'), + (138, 'Interpersonal Skills'), + (139, 'Communication Skills'), + (140, 'Customer Relationship Management'), + (141, 'Sales Techniques'), + (142, 'Market Research'), + (143, 'Digital Marketing'), + (144, 'Social Media Marketing'), + (145, 'Search Engine Optimization'), + (146, 'Content Marketing'), + (147, 'Data Analytics'), + (148, 'Financial Analysis'), + (149, 'Budgeting'), + (150, 'Project Planning'), + (151, 'Quality Assurance'), + (152, 'Risk Management'), + (153, 'Supply Chain Management'), + (154, 'Logistics'), + (155, 'Business Strategy'), + (156, 'Entrepreneurship'), + (157, 'Innovation'), + (158, 'Customer Service'), + (159, 'Hospitality'), + (160, 'Event Management'); +INSERT INTO skills (id, skill) +VALUES (161, 'Teaching'), + (162, 'Tutoring'), + (163, 'Curriculum Development'), + (164, 'Instructional Design'), + (165, 'Classroom Management'), + (166, 'Educational Technology'), + (167, 'Language Teaching'), + (168, 'Music'), + (169, 'Art'), + (170, 'Sports'), + (171, 'Fitness'), + (172, 'Nutrition'), + (173, 'Counseling'), + (174, 'Life Coaching'), + (175, 'Meditation'), + (176, 'Mindfulness'), + (177, 'Research'), + (178, 'Data Entry'), + (179, 'Virtual Assistance'), + (180, 'Project Coordination'), + (181, 'Event Coordination'), + (182, 'Office Management'), + (183, 'Translation'), + (184, 'Transcription'), + (185, 'Proofreading'), + (186, 'Editing'), + (187, 'Copywriting'), + (188, 'Content Creation'), + (189, 'Social Media Management'), + (190, 'Graphic Design'), + (191, 'Video Editing'), + (192, 'Photography'), + (193, 'Web Development'), + (194, 'Mobile App Development'), + (195, 'Game Development'), + (196, 'UI/UX Design'), + (197, 'Product Management'), + (198, 'Agile Methodology'), + (199, 'Scrum'), + (200, 'Lean Manufacturing'); \ No newline at end of file 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 ( diff --git a/src/main/resources/db/changelog/db.changelog-master.yaml b/src/main/resources/db/changelog/db.changelog-master.yaml index f09e9a9..49cbdae 100644 --- a/src/main/resources/db/changelog/db.changelog-master.yaml +++ b/src/main/resources/db/changelog/db.changelog-master.yaml @@ -23,9 +23,12 @@ databaseChangeLog: file: db/changelog/changeset/V5/user-info-schema-V5.sql - include: file: db/changelog/changeset/V5/data-V5.sql + - include: + file: db/changelog/changeset/V5/skills-data.sql - include: file: db/changelog/changeset/V5/dataSkill-V5.sql - include: file: db/changelog/changeset/V5/dataKudos-V5.sql - include: file: db/changelog/changeset/V5/deleted-user-schema.sql + From 5c0af4994645584fc84ec7817546b2b18c601a76 Mon Sep 17 00:00:00 2001 From: Maslyna Date: Wed, 24 May 2023 12:17:36 +0200 Subject: [PATCH 19/19] MERGE UPDATE --- .../kudos/service/KudosService.java | 154 +++++++++--------- 1 file changed, 77 insertions(+), 77 deletions(-) diff --git a/src/main/java/com/provedcode/kudos/service/KudosService.java b/src/main/java/com/provedcode/kudos/service/KudosService.java index fca1603..793441a 100644 --- a/src/main/java/com/provedcode/kudos/service/KudosService.java +++ b/src/main/java/com/provedcode/kudos/service/KudosService.java @@ -138,85 +138,85 @@ public KudosAmountWithSponsor getProofKudos(long proofId, Authentication authent } } - 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)); - }); + 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 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()); + 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)); + }); + } - @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); + 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); + } }