Skip to content
Snippets Groups Projects
Commit ad835a48 authored by Jan Pokorný's avatar Jan Pokorný :lifter_tone2:
Browse files

Merge branch 'M2' into 'M2-language-school-course-lecture'

# Conflicts:
#   application/model/src/main/java/org/fuseri/model/dto/course/CourseDto.java
parents e54e5b84 ffe59998
No related branches found
No related tags found
3 merge requests!31M2,!27Draft: M2 user,!20M2 language school course lecture
Pipeline #
Showing
with 416 additions and 103 deletions
package org.fuseri.model.dto.certificate;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Getter;
import lombok.Setter;
import org.fuseri.model.dto.common.DomainObjectDto;
import java.time.Instant;
/**
* This class represents a Data Transfer Object (DTO) for Certificate entities.
* It is used to transfer Certificate data between different layers of the application.
* It extends the DomainObjectDto class and includes additional Certificate-specific fields.
*/
@Getter
@Setter
public class CertificateSimpleDto extends DomainObjectDto {
@NotBlank
@NotNull
private Long userId;
@NotBlank
@NotNull
private Instant generatedAt;
@NotBlank
@NotNull
private Long courseId;
@NotBlank
@NotNull
private String certificateFile;
@NotBlank
@NotNull
private String certificateFileName;
public CertificateSimpleDto(Long id, Long userId, Instant generatedAt, Long courseId, String certificateFile, String certificateFileName) {
this.setId(id);
this.userId = userId;
this.generatedAt = generatedAt;
this.courseId = courseId;
this.certificateFile = certificateFile;
this.certificateFileName = certificateFileName;
}
}
...@@ -61,6 +61,18 @@ ...@@ -61,6 +61,18 @@
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId> <artifactId>spring-boot-starter-validation</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
</dependencies> </dependencies>
<build> <build>
......
package org.fuseri.modulecertificate;
import jakarta.persistence.*;
import java.io.Serializable;
import java.time.Instant;
import java.util.Objects;
@Entity
@Table(name = "certificate")
public class Certificate implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id_certificate")
private Long id;
@Column(name = "id_user")
private Long userId;
@Column(name = "generatedAt")
@Temporal(TemporalType.TIMESTAMP)
private Instant generatedAt;
@Column(name = "id_course")
private Long courseId;
@Column(name = "certificate_file")
private String certificateFile;
@Column(name = "certificate_file_name")
private String certificateFileName;
public Certificate() {
}
public Certificate(Long userId, Instant generatedAt, Long courseId, String certificateFile, String certificateFileName) {
this.userId = userId;
this.generatedAt = generatedAt;
this.courseId = courseId;
this.certificateFile = certificateFile;
this.certificateFileName = certificateFileName;
}
public Long getId() {
return id;
}
public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
public Instant getGeneratedAt() {
return generatedAt;
}
public void setGeneratedAt(Instant generatedAt) {
this.generatedAt = generatedAt;
}
public Long getCourseId() {
return courseId;
}
public void setCourseId(Long courseId) {
this.courseId = courseId;
}
public String getCertificateFile() {
return certificateFile;
}
public void setCertificateFile(String certificateFile) {
this.certificateFile = certificateFile;
}
public String getCertificateFileName() {
return certificateFileName;
}
public void setCertificateFileName(String certificateFileName) {
this.certificateFileName = certificateFileName;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof Certificate certificate)) {
return false;
}
return Objects.equals(getUserId(), certificate.getUserId())
&& Objects.equals(getGeneratedAt(), certificate.getGeneratedAt())
&& Objects.equals(getCourseId(), certificate.getCourseId())
&& Objects.equals(getCertificateFile(), certificate.getCertificateFile())
&& Objects.equals(getCertificateFileName(), certificate.getCertificateFileName());
}
@Override
public int hashCode() {
return Objects.hash(getUserId(), getGeneratedAt(), getCourseId(), getCertificateFile(), getCertificateFileName());
}
}
...@@ -3,8 +3,11 @@ package org.fuseri.modulecertificate.service; ...@@ -3,8 +3,11 @@ package org.fuseri.modulecertificate.service;
import jakarta.validation.Valid; import jakarta.validation.Valid;
import org.fuseri.model.dto.certificate.CertificateCreateDto; import org.fuseri.model.dto.certificate.CertificateCreateDto;
import org.fuseri.model.dto.certificate.CertificateDto; import org.fuseri.model.dto.certificate.CertificateDto;
import org.fuseri.model.dto.certificate.CertificateSimpleDto;
import org.fuseri.model.dto.common.Result; import org.fuseri.model.dto.common.Result;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import java.util.List; import java.util.List;
...@@ -18,10 +21,14 @@ import java.util.List; ...@@ -18,10 +21,14 @@ import java.util.List;
@RequestMapping("/certificates") @RequestMapping("/certificates")
public class CertificateController { public class CertificateController {
private final CertificateFacade certificateFacade;
@Autowired @Autowired
public CertificateController() { public CertificateController(CertificateFacade certificateFacade) {
this.certificateFacade = certificateFacade;
} }
/** /**
* Generates certificate for specified user and course. * Generates certificate for specified user and course.
* *
...@@ -29,8 +36,8 @@ public class CertificateController { ...@@ -29,8 +36,8 @@ public class CertificateController {
* @return CertificateDto with data of generated certificate * @return CertificateDto with data of generated certificate
*/ */
@PostMapping("/generate") @PostMapping("/generate")
public CertificateDto generate(@Valid @RequestBody CertificateCreateDto certificateCreateDto) { public CertificateSimpleDto generate(@Valid @RequestBody CertificateCreateDto certificateCreateDto) {
return new CertificateDto(); return certificateFacade.generate(certificateCreateDto);
} }
/** /**
...@@ -40,8 +47,8 @@ public class CertificateController { ...@@ -40,8 +47,8 @@ public class CertificateController {
* @return CertificateDto with data of previously generated certificate with specified ID * @return CertificateDto with data of previously generated certificate with specified ID
*/ */
@GetMapping("/find") @GetMapping("/find")
public CertificateDto find(@RequestParam String id) { public CertificateSimpleDto find(@RequestParam Long id) {
return new CertificateDto(); return certificateFacade.findById(id);
} }
/** /**
...@@ -52,8 +59,8 @@ public class CertificateController { ...@@ -52,8 +59,8 @@ public class CertificateController {
* for specified User. * for specified User.
*/ */
@GetMapping("/findForUser") @GetMapping("/findForUser")
public List<CertificateDto> findForUser(@RequestParam String userId) { public List<CertificateSimpleDto> findForUser(@RequestParam Long userId) {
return List.of(new CertificateDto()); return certificateFacade.findByUserId(userId);
} }
/** /**
...@@ -62,11 +69,11 @@ public class CertificateController { ...@@ -62,11 +69,11 @@ public class CertificateController {
* @param userId ID of user to retrieve certificates for. * @param userId ID of user to retrieve certificates for.
* @param courseId ID of course to retrieve certificates for. * @param courseId ID of course to retrieve certificates for.
* @return List of CertificateDto objects with previously generated certificates * @return List of CertificateDto objects with previously generated certificates
* for specified User. * for specified User and Course.
*/ */
@GetMapping("/getId") @GetMapping("/findForUserAndCourse")
public String getId(@RequestParam String userId, @RequestParam String courseId) { public List<CertificateSimpleDto> findForUserAndCourse(@RequestParam Long userId, @RequestParam Long courseId) {
return "0"; return certificateFacade.findByUserIdAndCourseId(userId, courseId);
} }
/** /**
...@@ -75,17 +82,17 @@ public class CertificateController { ...@@ -75,17 +82,17 @@ public class CertificateController {
* @param id Id of certificate to be deleted. * @param id Id of certificate to be deleted.
*/ */
@DeleteMapping("/delete") @DeleteMapping("/delete")
public void delete(@RequestParam String id) { public void delete(@RequestParam Long id) {
certificateFacade.deleteCertificate(id);
} }
/** /**
* Find certificates and return them in a paginated format * Find certificates and return them in a paginated format
* *
* @param page the page number of the certificates to retrieve
* @return a Result object containing a list of CertificateDto objects and pagination information * @return a Result object containing a list of CertificateDto objects and pagination information
*/ */
@GetMapping("/findAll") @GetMapping("/findAll")
public Result<CertificateDto> findAllCertificates(@RequestParam int page) { public Page<CertificateSimpleDto> findAllCertificates(@RequestBody Pageable pageable) {
return new Result<>(); return certificateFacade.findAll(pageable);
} }
} }
package org.fuseri.modulecertificate.service;
import org.fuseri.model.dto.certificate.CertificateCreateDto;
import org.fuseri.model.dto.certificate.CertificateDto;
import org.fuseri.model.dto.certificate.CertificateSimpleDto;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Service
@Transactional
public class CertificateFacade {
private final CertificateService certificateService;
private final CertificateMapper certificateMapper;
@Autowired
public CertificateFacade(CertificateService certificateService, CertificateMapper certificateMapper) {
this.certificateService = certificateService;
this.certificateMapper = certificateMapper;
}
@Cacheable(cacheNames = "certificates", key = "#id")
@Transactional(readOnly = true)
public CertificateSimpleDto findById(Long id) {
return certificateMapper.mapToSimpleDto(certificateService.findById(id));
}
@Transactional()
public CertificateSimpleDto generate(CertificateCreateDto certificateCreateDto) {
return certificateMapper.mapToSimpleDto(certificateService.save(certificateMapper.mapToCertificate(certificateCreateDto)));
}
@Cacheable(cacheNames = "certificates", key = "#userId")
@Transactional(readOnly = true)
public List<CertificateSimpleDto> findByUserId(Long userId) {
return certificateMapper.mapToList(certificateService.findByUserId(userId));
}
@Cacheable(cacheNames = "certificates", key = "#userId")
@Transactional(readOnly = true)
public List<CertificateSimpleDto> findByUserIdAndCourseId(Long userId, Long courseId) {
return certificateMapper.mapToList(certificateService.findByUserIdAndCourseId(userId, courseId));
}
@Transactional(readOnly = true)
public Page<CertificateSimpleDto> findAll(Pageable pageable) {
return certificateMapper.mapToPageDto(certificateService.findAll(pageable));
}
@Transactional(readOnly = true)
public void deleteCertificate(Long certificateId) {
certificateService.delete(certificateId);
}
}
package org.fuseri.modulecertificate.service;
import org.fuseri.model.dto.certificate.CertificateCreateDto;
import org.fuseri.model.dto.certificate.CertificateDto;
import org.fuseri.model.dto.certificate.CertificateSimpleDto;
import org.fuseri.modulecertificate.Certificate;
import org.mapstruct.Mapper;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import java.time.Instant;
import java.util.List;
@Mapper(componentModel = "spring")
public interface CertificateMapper {
default CertificateSimpleDto mapToSimpleDto(Certificate certificate)
{
if ( certificate == null ) {
return null;
}
return new CertificateSimpleDto(
certificate.getId(),
certificate.getUserId(),
certificate.getGeneratedAt(),
certificate.getCourseId(),
certificate.getCertificateFile(),
certificate.getCertificateFileName());
}
default Certificate mapToCertificate(CertificateCreateDto certificateCreateDto)
{
if ( certificateCreateDto == null ) {
return null;
}
return new Certificate(certificateCreateDto.getUser().getId(),
Instant.now(),
certificateCreateDto.getCourse().getId(),
null,
null);
}
List<CertificateSimpleDto> mapToList(List<Certificate> certificates);
default Page<CertificateSimpleDto> mapToPageDto(Page<Certificate> certificates) {
return new PageImpl<>(mapToList(certificates.getContent()), certificates.getPageable(), certificates.getTotalPages());
}
}
package org.fuseri.modulecertificate.service;
import org.fuseri.modulecertificate.Certificate;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface CertificateRepository extends JpaRepository<Certificate, Long> {
@Query("SELECT c FROM Certificate c WHERE c.userId = ?1")
List<Certificate> findCertificateByUserId(Long userId);
@Query("SELECT c FROM Certificate c WHERE c.userId = ?1 and c.courseId = ?2")
List<Certificate> findCertificateByUserIdAndCourseId(Long userId, Long courseId);
}
package org.fuseri.modulecertificate.service;
import org.fuseri.modulecertificate.Certificate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Service
public class CertificateService {
private final CertificateRepository certificateRepository;
@Autowired
public CertificateService(CertificateRepository certificateRepository) {
this.certificateRepository = certificateRepository;
}
@Transactional(readOnly = true)
public Certificate findById(Long id) {
return certificateRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("Certificate with id: " + id + " was not found."));
}
@Transactional(readOnly = true)
public List<Certificate> findByUserId(Long userId) {
return certificateRepository.findCertificateByUserId(userId);
}
@Transactional(readOnly = true)
public List<Certificate> findByUserIdAndCourseId(Long userId, Long courseId) {
return certificateRepository.findCertificateByUserIdAndCourseId(userId, courseId);
}
@Transactional(readOnly = true)
public Certificate save(Certificate certificate) {
return certificateRepository.save(certificate);
}
@Transactional(readOnly = true)
public void delete(Long certificateId) {
certificateRepository.deleteById(certificateId);
}
@Transactional(readOnly = true)
public Page<Certificate> findAll(Pageable pageable) {
return certificateRepository.findAll(pageable);
}
}
package org.fuseri.modulecertificate.service;
public class ResourceNotFoundException extends RuntimeException {
public ResourceNotFoundException() {
}
public ResourceNotFoundException(String message) {
super(message);
}
public ResourceNotFoundException(String message, Throwable cause) {
super(message, cause);
}
public ResourceNotFoundException(Throwable cause) {
super(cause);
}
public ResourceNotFoundException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
server.port=5001 server.port=5001
\ No newline at end of file
spring.jpa.open-in-view=false
spring.datasource.url=jdbc:h2:mem:social-network;MODE=PostgreSQL
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=SedaQ-app
spring.datasource.password=$argon2id$v=19$m=16,t=2,p=1$YmF0bWFuYmF0bWFu$MdHYB359HdivAb9J6CaILw
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
# showing SQL is generally good practice for running project locally to check whether there is not an issue with implementation of JPA methods.
spring.jpa.show-sql=true
\ No newline at end of file
...@@ -9,12 +9,16 @@ import org.fuseri.model.dto.course.ProficiencyLevelDto; ...@@ -9,12 +9,16 @@ import org.fuseri.model.dto.course.ProficiencyLevelDto;
import org.fuseri.model.dto.user.AddressDto; import org.fuseri.model.dto.user.AddressDto;
import org.fuseri.model.dto.user.UserDto; import org.fuseri.model.dto.user.UserDto;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springdoc.core.converters.models.Pageable;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.PageRequest;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MockMvc;
import java.util.List;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
...@@ -72,9 +76,9 @@ class CertificateControllerTests { ...@@ -72,9 +76,9 @@ class CertificateControllerTests {
.contentType(MediaType.APPLICATION_JSON)) .contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk()).andReturn().getResponse().getContentAsString(); .andExpect(status().isOk()).andReturn().getResponse().getContentAsString();
String id = objectMapper.readValue(response, CertificateDto.class).getId(); Long id = objectMapper.readValue(response, CertificateDto.class).getId();
mockMvc.perform(get("/certificates/find").param("id", id)) mockMvc.perform(get("/certificates/find").param("id", id.toString()))
.andExpect(status().isOk()) .andExpect(status().isOk())
.andExpect(jsonPath("$.id").value(id)); .andExpect(jsonPath("$.id").value(id));
} }
...@@ -101,30 +105,30 @@ class CertificateControllerTests { ...@@ -101,30 +105,30 @@ class CertificateControllerTests {
@Test @Test
void findCertificateIdForUserAndCourse() throws Exception { void findCertificateIdForUserAndCourse() throws Exception {
mockMvc.perform(get("/certificates/getId") mockMvc.perform(get("/certificates/findForUserAndCourse")
.param("userId", "0") .param("userId", "0")
.param("courseId", "0")) .param("courseId", "0"))
.andExpect(status().isOk()) .andExpect(status().isOk())
.andExpect(content().string("0")); .andExpect(content().string("[]"));
} }
@Test @Test
void findCertificateIdWithoutUserId() throws Exception { void findCertificateIdWithoutUserId() throws Exception {
mockMvc.perform(get("/certificates/getId") mockMvc.perform(get("/certificates/findForUserAndCourse")
.param("courseId", "0")) .param("courseId", "0"))
.andExpect(status().is4xxClientError()); .andExpect(status().is4xxClientError());
} }
@Test @Test
void findCertificateIdWithoutCourseId() throws Exception { void findCertificateIdWithoutCourseId() throws Exception {
mockMvc.perform(get("/certificates/getId") mockMvc.perform(get("/certificates/findForUserAndCourse")
.param("userId", "0")) .param("userId", "0"))
.andExpect(status().is4xxClientError()); .andExpect(status().is4xxClientError());
} }
@Test @Test
void findCertificateIdWithoutParams() throws Exception { void findCertificateIdWithoutParams() throws Exception {
mockMvc.perform(get("/certificates/getId")) mockMvc.perform(get("/certificates/findForUserAndCourse"))
.andExpect(status().is4xxClientError()); .andExpect(status().is4xxClientError());
} }
...@@ -144,8 +148,14 @@ class CertificateControllerTests { ...@@ -144,8 +148,14 @@ class CertificateControllerTests {
@Test @Test
void findAllCertificates() throws Exception { void findAllCertificates() throws Exception {
mockMvc.perform(post("/certificates/generate")
.content(asJsonString(new CertificateCreateDto(USER, COURSE)))
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk());
mockMvc.perform(get("/certificates/findAll") mockMvc.perform(get("/certificates/findAll")
.param("page", "1")) .content("{ \"page\": 0, \"size\": 1, \"sort\": []}")
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk()); .andExpect(status().isOk());
} }
......
package org.fuseri.modulecertificate;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class ModuleCertificateApplicationTests {
@Test
void contextLoads() {
}
}
package org.fuseri.modulecertificate;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.fuseri.model.dto.certificate.CertificateCreateDto;
import org.fuseri.model.dto.certificate.CertificateDto;
import org.fuseri.model.dto.course.CourseDto;
import org.fuseri.model.dto.course.LanguageTypeDto;
import org.fuseri.model.dto.course.ProficiencyLevelDto;
import org.fuseri.model.dto.user.AddressDto;
import org.fuseri.model.dto.user.UserDto;
import org.fuseri.modulecertificate.service.CertificateController;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@SpringBootTest
@AutoConfigureMockMvc
class ModuleCertificateCertificateControllerTests {
@Autowired
private MockMvc mockMvc;
@Autowired
ObjectMapper objectMapper;
@Test
void generateCertificate() throws Exception {
CertificateDto expectedResponse = new CertificateDto();
String response = mockMvc.perform(post("/certificates/generate")
.content(asJsonString(new CertificateCreateDto(
new UserDto("novakovat","novakova@gamil.com", "Tereza",
"Nováková", new AddressDto()),
new CourseDto("AJ1", 10, LanguageTypeDto.ENGLISH, ProficiencyLevelDto.A1))))
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andReturn().getResponse().getContentAsString();
CertificateController certificateController = objectMapper.readValue(response, CertificateController.class);
assertThat("response", certificateController.find("0"), is(instanceOf(expectedResponse.getClass())));
}
@Test
void deleteCertificate() throws Exception {
String response = mockMvc.perform(delete("/certificates/delete")
.param("id", "1"))
.andExpect(status().isOk())
.andReturn().getResponse().getContentAsString();
}
public static String asJsonString(final Object obj) {
try {
return new ObjectMapper().writeValueAsString(obj);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment