Skip to content

Commit 2d98ca6

Browse files
Denis973LordRenDS
andauthored
S2 us2 5 8 (#66)
* add information in DB * add sorting proofs by date of creation * returned OpenAPI * remove the proof of the specified talent * proof creation * refactor validation: create class ValidateTalentForCompliance * Little optimize and cleanup code, rename "sortDir" to "orderBy" * Little optimize and cleanup code --------- Co-authored-by: Ren <sergeysolovyov2016@gmail.com> Co-authored-by: Ren <75202059+LordRenDS@users.noreply.github.com>
1 parent fc8f2d5 commit 2d98ca6

11 files changed

Lines changed: 322 additions & 100 deletions

File tree

pom.xml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,14 @@
1717
<java.version>17</java.version>
1818
</properties>
1919
<dependencies>
20+
21+
<!-- https://mvnrepository.com/artifact/org.springdoc/springdoc-openapi-starter-webmvc-ui -->
22+
<dependency>
23+
<groupId>org.springdoc</groupId>
24+
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
25+
<version>2.0.0</version>
26+
</dependency>
27+
2028
<dependency>
2129
<groupId>org.springframework.boot</groupId>
2230
<artifactId>spring-boot-starter-data-jpa</artifactId>

src/main/java/com/provedcode/ProvedCodeApplication.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@
66

77
@SpringBootApplication
88
@ConfigurationPropertiesScan
9-
public class ProvedCodeApplication {
9+
public class ProvedCodeApplication {
1010

11-
public static void main(String[] args) {
12-
SpringApplication.run(ProvedCodeApplication.class, args);
13-
}
11+
public static void main(String[] args) {
12+
SpringApplication.run(ProvedCodeApplication.class, args);
13+
}
1414

1515
}

src/main/java/com/provedcode/config/PageProperties.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@
1616
@Slf4j
1717
public record PageProperties(
1818
int defaultPageNum,
19-
int defaultPageSize
19+
int defaultPageSize,
20+
String defaultSortBy
2021
) {
2122
@PostConstruct
2223
void print() {

src/main/java/com/provedcode/config/SecurityConfig.java

Lines changed: 23 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -44,33 +44,37 @@
4444
@EnableWebSecurity
4545
@EnableMethodSecurity
4646
public class SecurityConfig {
47+
//http://localhost:8080/swagger-ui/index.html
4748
@Bean
4849
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
4950
http.authorizeHttpRequests(c -> c
5051
.requestMatchers("/actuator/health").permitAll() // for DevOps
5152
.requestMatchers(antMatcher("/h2/**")).permitAll()
5253
.requestMatchers(antMatcher("/api/talents/**")).permitAll()
5354
.requestMatchers(antMatcher("/error")).permitAll()
55+
.requestMatchers(antMatcher("/v3/api-docs/**")).permitAll() // for openAPI
56+
.requestMatchers(antMatcher("/swagger-ui/**")).permitAll() // for openAPI
57+
.requestMatchers(antMatcher("/swagger-ui.html")).permitAll() // for openAPI
5458
.anyRequest().authenticated()
5559
);
5660

5761
http.exceptionHandling(c -> c
5862
.authenticationEntryPoint((request, response, authException) -> {
59-
log.info("Authentication failed {}, message:{}",
60-
describe(request),
61-
authException.getMessage());
62-
response.sendError(
63-
HttpStatus.UNAUTHORIZED.value(),
64-
authException.getMessage());
65-
}
63+
log.info("Authentication failed {}, message:{}",
64+
describe(request),
65+
authException.getMessage());
66+
response.sendError(
67+
HttpStatus.UNAUTHORIZED.value(),
68+
authException.getMessage());
69+
}
6670
)
6771
.accessDeniedHandler((request, response, accessDeniedException) -> {
68-
log.info("Authorization failed {},message: {}",
69-
describe(request),
70-
accessDeniedException.getMessage());
71-
response.sendError(HttpStatus.FORBIDDEN.value(),
72-
accessDeniedException.getMessage());
73-
}
72+
log.info("Authorization failed {},message: {}",
73+
describe(request),
74+
accessDeniedException.getMessage());
75+
response.sendError(HttpStatus.FORBIDDEN.value(),
76+
accessDeniedException.getMessage());
77+
}
7478
)
7579
);
7680

@@ -82,10 +86,10 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti
8286
http.sessionManagement().sessionCreationPolicy(STATELESS);
8387

8488
http.oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt)
85-
.exceptionHandling(c -> c
86-
.authenticationEntryPoint(new BearerTokenAuthenticationEntryPoint())
87-
.accessDeniedHandler(new BearerTokenAccessDeniedHandler())
88-
);
89+
.exceptionHandling(c -> c
90+
.authenticationEntryPoint(new BearerTokenAuthenticationEntryPoint())
91+
.accessDeniedHandler(new BearerTokenAccessDeniedHandler())
92+
);
8993

9094
return http.build();
9195
}
@@ -135,8 +139,8 @@ UserDetailsService userDetailsService(
135139
UserInfoMapper mapper
136140
) {
137141
return login -> repository.findByLogin(login)
138-
.map(mapper::toUserDetails)
139-
.orElseThrow(() -> new UsernameNotFoundException(login + " not found"));
142+
.map(mapper::toUserDetails)
143+
.orElseThrow(() -> new UsernameNotFoundException(login + " not found"));
140144
}
141145

142146
@Bean

src/main/java/com/provedcode/talent/controller/TalentProofController.java

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
11
package com.provedcode.talent.controller;
22

33
import com.provedcode.talent.mapper.TalentProofMapper;
4+
import com.provedcode.talent.model.dto.AddProofDTO;
45
import com.provedcode.talent.model.dto.FullProofDTO;
56
import com.provedcode.talent.model.dto.ProofDTO;
67
import com.provedcode.talent.service.TalentProofService;
8+
import com.provedcode.user.model.dto.SessionInfoDTO;
79
import lombok.AllArgsConstructor;
810
import org.springframework.data.domain.Page;
11+
import org.springframework.http.ResponseEntity;
12+
import org.springframework.security.access.prepost.PreAuthorize;
13+
import org.springframework.security.core.Authentication;
14+
import org.springframework.web.bind.annotation.*;
915
import org.springframework.security.access.prepost.PreAuthorize;
1016
import org.springframework.security.core.Authentication;
1117
import org.springframework.web.bind.annotation.*;
@@ -21,8 +27,25 @@ public class TalentProofController {
2127

2228
@GetMapping("/proofs")
2329
Page<ProofDTO> getAllProofs(@RequestParam(value = "page") Optional<Integer> page,
24-
@RequestParam(value = "size") Optional<Integer> size) {
25-
return talentProofService.getAllProofsPage(page, size).map(talentProofMapper::toProofDTO);
30+
@RequestParam(value = "size") Optional<Integer> size,
31+
@RequestParam(value = "order-by") Optional<String> orderBy) {
32+
return talentProofService.getAllProofsPage(page, size, orderBy).map(talentProofMapper::toProofDTO);
33+
}
34+
35+
@PreAuthorize("hasRole('TALENT')")
36+
@DeleteMapping("/{talent-id}/proofs/{proof-id}")
37+
SessionInfoDTO deleteProof(@PathVariable(value = "talent-id") long talentId,
38+
@PathVariable(value = "proof-id") long proofId,
39+
Authentication authentication) {
40+
return talentProofService.deleteProofById(talentId, proofId, authentication);
41+
}
42+
43+
@PreAuthorize("hasRole('TALENT')")
44+
@PostMapping("/{talent-id}/proofs")
45+
ResponseEntity<?> addProof(@PathVariable(value = "talent-id") long talentId,
46+
@RequestBody AddProofDTO addProofDTO,
47+
Authentication authentication) {
48+
return talentProofService.addProof(addProofDTO, talentId, authentication);
2649
}
2750

2851
@GetMapping("/{talent-id}/proofs")
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package com.provedcode.talent.model.dto;
2+
3+
public record AddProofDTO(
4+
String link,
5+
String text
6+
) {
7+
}

src/main/java/com/provedcode/talent/service/TalentProofService.java

Lines changed: 94 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,27 +2,36 @@
22

33
import com.provedcode.config.PageProperties;
44
import com.provedcode.talent.model.ProofStatus;
5+
import com.provedcode.talent.model.dto.AddProofDTO;
56
import com.provedcode.talent.model.dto.FullProofDTO;
67
import com.provedcode.talent.model.dto.ProofDTO;
78
import com.provedcode.talent.model.entity.Talent;
89
import com.provedcode.talent.model.entity.TalentProof;
910
import com.provedcode.talent.repo.TalentProofRepository;
1011
import com.provedcode.talent.repo.TalentRepository;
12+
import com.provedcode.user.model.dto.SessionInfoDTO;
1113
import com.provedcode.user.model.entity.UserInfo;
1214
import com.provedcode.user.repo.UserInfoRepository;
15+
import com.provedcode.utill.ValidateTalentForCompliance;
1316
import lombok.AllArgsConstructor;
1417
import lombok.extern.slf4j.Slf4j;
1518
import org.springframework.data.domain.Page;
1619
import org.springframework.data.domain.PageRequest;
1720
import org.springframework.data.domain.Sort;
1821
import org.springframework.http.HttpStatus;
22+
import org.springframework.http.ResponseEntity;
1923
import org.springframework.security.core.Authentication;
2024
import org.springframework.stereotype.Service;
25+
import org.springframework.transaction.annotation.Transactional;
2126
import org.springframework.web.server.ResponseStatusException;
27+
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
2228

29+
import java.net.URI;
30+
import java.time.LocalDateTime;
2331
import java.util.Optional;
2432

2533
import static org.springframework.http.HttpStatus.BAD_REQUEST;
34+
import static org.springframework.http.HttpStatus.NOT_IMPLEMENTED;
2635

2736
@Service
2837
@AllArgsConstructor
@@ -32,27 +41,89 @@ public class TalentProofService {
3241
TalentRepository talentRepository;
3342
UserInfoRepository userInfoRepository;
3443
PageProperties pageProperties;
44+
ValidateTalentForCompliance validateTalentForCompliance;
3545

36-
public Page<TalentProof> getAllProofsPage(Optional<Integer> page, Optional<Integer> size) {
46+
public Page<TalentProof> getAllProofsPage(Optional<Integer> page, Optional<Integer> size,
47+
Optional<String> orderBy) {
3748
if (page.orElse(pageProperties.defaultPageNum()) < 0) {
3849
throw new ResponseStatusException(BAD_REQUEST, "'page' query parameter must be greater than or equal to 0");
3950
}
4051
if (size.orElse(pageProperties.defaultPageSize()) <= 0) {
4152
throw new ResponseStatusException(BAD_REQUEST, "'size' query parameter must be greater than or equal to 1");
4253
}
54+
55+
if (orderBy.isPresent()) {
56+
if (!orderBy.get().equalsIgnoreCase(Sort.Direction.ASC.name()) &&
57+
!orderBy.get().equalsIgnoreCase(Sort.Direction.DESC.name())) {
58+
throw new ResponseStatusException(BAD_REQUEST, "'orderBy' query parameter must be ASC or DESC");
59+
}
60+
Sort sort =
61+
orderBy.get().equalsIgnoreCase(Sort.Direction.ASC.name()) ? Sort.by(pageProperties.defaultSortBy())
62+
.ascending()
63+
: Sort.by(pageProperties.defaultSortBy())
64+
.descending();
65+
return talentProofRepository.findByStatus(ProofStatus.PUBLISHED,
66+
PageRequest.of(page.orElse(
67+
pageProperties.defaultPageNum()),
68+
size.orElse(
69+
pageProperties.defaultPageSize()), sort));
70+
}
4371
return talentProofRepository.findByStatus(ProofStatus.PUBLISHED,
44-
PageRequest.of(page.orElse(
45-
pageProperties.defaultPageNum()),
46-
size.orElse(
47-
pageProperties.defaultPageSize())));
72+
PageRequest.of(page.orElse(
73+
pageProperties.defaultPageNum()),
74+
size.orElse(
75+
pageProperties.defaultPageSize())));
76+
}
77+
78+
@Transactional
79+
public SessionInfoDTO deleteProofById(long talentId, long proofId, Authentication authentication) {
80+
81+
Optional<Talent> talent = talentRepository.findById(talentId);
82+
Optional<TalentProof> talentProof = talentProofRepository.findById(proofId);
83+
Optional<UserInfo> userInfo = userInfoRepository.findByLogin(authentication.getName());
84+
validateTalentForCompliance.userVerification(talent, talentProof, userInfo, talentId, proofId);
85+
talentProofRepository.delete(talentProof.orElseThrow(() -> new ResponseStatusException(NOT_IMPLEMENTED)));
86+
return new SessionInfoDTO("deleted", "null");
87+
}
88+
89+
public ResponseEntity<?> addProof(AddProofDTO addProofDTO, long talentId, Authentication authentication) {
90+
91+
Optional<Talent> talent = talentRepository.findById(talentId);
92+
Optional<UserInfo> userInfo = userInfoRepository.findByLogin(authentication.getName());
93+
94+
validateTalentForCompliance.userVerification(talent, userInfo, talentId);
95+
96+
TalentProof talentProof = TalentProof.builder()
97+
.talent(talent.get())
98+
.talentId(talentId)
99+
.link(addProofDTO.link())
100+
.text(addProofDTO.text())
101+
.status(ProofStatus.DRAFT)
102+
.created(LocalDateTime.now())
103+
.build();
104+
105+
talentProofRepository.save(talentProof);
106+
107+
URI location = ServletUriComponentsBuilder
108+
.fromCurrentRequest()
109+
.path("/{id}")
110+
.buildAndExpand(talentProof.getId())
111+
.toUri();
112+
113+
return ResponseEntity.created(location).build();
48114
}
49115

50116
public FullProofDTO getTalentProofs(Long talentId, Optional<Integer> page, Optional<Integer> size,
51-
Optional<String> direction, Authentication authentication, String... sortProperties) {
117+
Optional<String> direction, Authentication authentication,
118+
String... sortProperties) {
52119
Talent talent = talentRepository.findById(talentId)
53-
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Talent with id = %s not found".formatted(talentId)));
120+
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND,
121+
"Talent with id = %s not found".formatted(
122+
talentId)));
54123
UserInfo userInfo = userInfoRepository.findByLogin(authentication.getName())
55-
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Talent with id = %s not found".formatted(talentId)));
124+
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND,
125+
"Talent with id = %s not found".formatted(
126+
talentId)));
56127
Page<TalentProof> proofs;
57128
PageRequest pageRequest;
58129
String sortDirection = direction.orElseGet(Sort.DEFAULT_DIRECTION::name);
@@ -63,7 +134,8 @@ public FullProofDTO getTalentProofs(Long talentId, Optional<Integer> page, Optio
63134
if (size.orElse(pageProperties.defaultPageSize()) <= 0) {
64135
throw new ResponseStatusException(BAD_REQUEST, "'size' query parameter must be greater than or equal to 1");
65136
}
66-
if (!sortDirection.equalsIgnoreCase(Sort.Direction.ASC.name()) && !sortDirection.equalsIgnoreCase(Sort.Direction.DESC.name())) {
137+
if (!sortDirection.equalsIgnoreCase(Sort.Direction.ASC.name()) &&
138+
!sortDirection.equalsIgnoreCase(Sort.Direction.DESC.name())) {
67139
throw new ResponseStatusException(BAD_REQUEST, "'direction' query param must be equals ASC or DESC");
68140
}
69141

@@ -84,17 +156,17 @@ public FullProofDTO getTalentProofs(Long talentId, Optional<Integer> page, Optio
84156
}
85157

86158
return FullProofDTO.builder()
87-
.id(talent.getId())
88-
.image(talent.getImage())
89-
.firstName(talent.getFirstName())
90-
.lastName(talent.getLastName())
91-
.specialization(talent.getSpecialization())
92-
.proofs(proofs.map(i -> ProofDTO.builder()
93-
.id(i.getId())
94-
.created(i.getCreated().toString())
95-
.link(i.getLink())
96-
.text(i.getText())
97-
.status(i.getStatus()).build()))
98-
.build();
159+
.id(talent.getId())
160+
.image(talent.getImage())
161+
.firstName(talent.getFirstName())
162+
.lastName(talent.getLastName())
163+
.specialization(talent.getSpecialization())
164+
.proofs(proofs.map(i -> ProofDTO.builder()
165+
.id(i.getId())
166+
.created(i.getCreated().toString())
167+
.link(i.getLink())
168+
.text(i.getText())
169+
.status(i.getStatus()).build()))
170+
.build();
99171
}
100-
}
172+
}

0 commit comments

Comments
 (0)