CRUD tested

This commit is contained in:
Catalin Patrut
2021-08-08 13:39:11 +02:00
parent 4a3a9923ef
commit 42e1b15322
12 changed files with 136 additions and 48 deletions

36
pom.xml
View File

@@ -16,6 +16,7 @@
<properties> <properties>
<java.version>16</java.version> <java.version>16</java.version>
<swagger.version>3.0.0</swagger.version> <swagger.version>3.0.0</swagger.version>
<mapstruct.version>1.4.2.Final</mapstruct.version>
</properties> </properties>
<dependencies> <dependencies>
<dependency> <dependency>
@@ -77,7 +78,11 @@
<groupId>io.springfox</groupId> <groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId> <artifactId>springfox-swagger-ui</artifactId>
<version>3.0.0</version> <version>3.0.0</version>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>${mapstruct.version}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>io.springfox</groupId> <groupId>io.springfox</groupId>
@@ -100,6 +105,35 @@
</excludes> </excludes>
</configuration> </configuration>
</plugin> </plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${mapstruct.version}</version>
</path>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</path>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok-mapstruct-binding</artifactId>
<version>0.2.0</version>
</dependency>
</annotationProcessorPaths>
<compilerArgs>
<compilerArg>
-Amapstruct.defaultComponentModel=spring
</compilerArg>
</compilerArgs>
</configuration>
</plugin>
</plugins> </plugins>
</build> </build>

View File

@@ -2,26 +2,10 @@ package dev.enblng.api;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.RestController;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
@SpringBootApplication @SpringBootApplication
public class ApiApplication { public class ApiApplication {
public static void main(final String[] args) { public static void main(final String[] args) {
SpringApplication.run(ApiApplication.class, args); SpringApplication.run(ApiApplication.class, args);
} }
@Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.withClassAnnotation(RestController.class))
.paths(PathSelectors.any())
.build();
}
} }

View File

@@ -2,6 +2,7 @@ package dev.enblng.api.api;
import dev.enblng.api.dto.CommentTO; import dev.enblng.api.dto.CommentTO;
import dev.enblng.api.services.CommentService; import dev.enblng.api.services.CommentService;
import io.swagger.annotations.Api;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.PageRequest;
@@ -16,6 +17,7 @@ import java.util.UUID;
@RestController @RestController
@RequestMapping(CommentController.PATH) @RequestMapping(CommentController.PATH)
@Api(tags = "Comments")
@Slf4j @Slf4j
public class CommentController { public class CommentController {
static final String PATH = "comments"; static final String PATH = "comments";

View File

@@ -2,6 +2,7 @@ package dev.enblng.api.api;
import dev.enblng.api.dto.PostTO; import dev.enblng.api.dto.PostTO;
import dev.enblng.api.services.PostService; import dev.enblng.api.services.PostService;
import io.swagger.annotations.Api;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.PageRequest;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
@@ -15,6 +16,7 @@ import java.util.UUID;
@RestController @RestController
@RequestMapping(PostController.PATH) @RequestMapping(PostController.PATH)
@Api(tags = "Posts")
@Slf4j @Slf4j
public class PostController { public class PostController {

View File

@@ -3,10 +3,22 @@ package dev.enblng.api.config;
import org.modelmapper.ModelMapper; import org.modelmapper.ModelMapper;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.web.bind.annotation.RestController;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
//TODO: delete //TODO: delete
@Configuration @Configuration
public class BeanConfig { public class BeanConfig {
private static final String SNAPSHOT = "1.0";
private static final String HTTP_WWW_APACHE_ORG_LICENSES_LICENSE_2_0_HTML = "Usage of this code is forbidden without consent";
private static final String APACHE_2_0 = "Apache 2.0";
@Bean @Bean
public ModelMapper modelMapper() { public ModelMapper modelMapper() {
final ModelMapper modelMapper = new ModelMapper(); final ModelMapper modelMapper = new ModelMapper();
@@ -17,5 +29,26 @@ public class BeanConfig {
return modelMapper; return modelMapper;
} }
@Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.useDefaultResponseMessages(false)
.select()
.apis(RequestHandlerSelectors.withClassAnnotation(RestController.class))
.paths(PathSelectors.any())
.build()
.apiInfo(apiEndPointsInfo());
}
private ApiInfo apiEndPointsInfo() {
return new ApiInfoBuilder().title("Blog API")
.description("Example of a blog API")
.contact(new Contact("Catalin Patrut", "NO URL", "api@cpatrut.ro"))
.license(APACHE_2_0)
.licenseUrl(HTTP_WWW_APACHE_ORG_LICENSES_LICENSE_2_0_HTML)
.version(SNAPSHOT)
.build();
}
} }

View File

@@ -26,11 +26,11 @@ public class CommentTO {
@Size(min = 5, max = 255) @Size(min = 5, max = 255)
String title; String title;
@ApiModelProperty(position = 2, value = ONLY_FOR_GET_MESSAGE) @ApiModelProperty(position = 2, value = ONLY_FOR_GET_MESSAGE, example = "George")
@NotNull @NotNull
String author; String author;
@ApiModelProperty(position = 3, required = true) @ApiModelProperty(position = 3, required = true, example = "Hello hello")
@Size(min = 10) @Size(min = 10)
@NotNull @NotNull
String content; String content;
@@ -38,8 +38,6 @@ public class CommentTO {
@ApiModelProperty(position = 5, value = ONLY_FOR_GET_MESSAGE) @ApiModelProperty(position = 5, value = ONLY_FOR_GET_MESSAGE)
String updateTime; String updateTime;
@ApiModelProperty(position = 4, value = ONLY_FOR_GET_MESSAGE)
String creationTime;
@ApiModelProperty(position = 6, value = FOREIGN_KEY_MESSAGE) @ApiModelProperty(position = 6, value = FOREIGN_KEY_MESSAGE)
UUID postId; UUID postId;
@@ -50,10 +48,9 @@ public class CommentTO {
final String author, final String author,
final String content, final String content,
final String updateTime, final String updateTime,
final String creationTime,
final UUID postId final UUID postId
) { ) {
return new CommentTO(id, title, author, content, updateTime, creationTime, postId); return new CommentTO(id, title, author, content, updateTime, postId);
} }
} }

View File

@@ -22,7 +22,7 @@ public class PostTO {
@ApiModelProperty(value = UUID_MESSAGE) @ApiModelProperty(value = UUID_MESSAGE)
UUID id; UUID id;
@ApiModelProperty(position = 1, required = true) @ApiModelProperty(position = 1, required = true, example = "Hello title")
@Size(min = 5, max = 255) @Size(min = 5, max = 255)
@NotNull @NotNull
String title; String title;
@@ -30,7 +30,7 @@ public class PostTO {
@ApiModelProperty(value = ONLY_FOR_GET_MESSAGE, position = 2) @ApiModelProperty(value = ONLY_FOR_GET_MESSAGE, position = 2)
String author; String author;
@ApiModelProperty(position = 3, required = true) @ApiModelProperty(position = 3, required = true, example = "Hello hello")
@Size(min = 10) @Size(min = 10)
@NotNull @NotNull
String content; String content;
@@ -39,18 +39,13 @@ public class PostTO {
@Null @Null
String updateTime; String updateTime;
@ApiModelProperty(value = ONLY_FOR_GET_MESSAGE, position = 4)
@Null
String creationTime;
@Builder @Builder
private static PostTO newPostTo(final UUID id, private static PostTO newPostTo(final UUID id,
final String title, final String title,
final String author, final String author,
final String content, final String content,
final String updateTime, final String updateTime
final String creationTime
) { ) {
return new PostTO(id, title, author, content, updateTime, creationTime); return new PostTO(id, title, author, content, updateTime);
} }
} }

View File

@@ -0,0 +1,14 @@
package dev.enblng.api.mappers;
import dev.enblng.api.dto.CommentTO;
import dev.enblng.api.entities.CommentEntity;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
@Mapper
public interface CommentMapper {
@Mapping(target = "creationTime", ignore = true)
CommentEntity toEntity(final CommentTO comment);
CommentTO toTransferObject(final CommentEntity comment);
}

View File

@@ -0,0 +1,14 @@
package dev.enblng.api.mappers;
import dev.enblng.api.dto.PostTO;
import dev.enblng.api.entities.PostEntity;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
@Mapper
public interface PostMapper {
@Mapping(target = "creationTime", ignore = true)
PostEntity toEntity(final PostTO post);
PostTO toTransferObject(final PostEntity post);
}

View File

@@ -25,5 +25,5 @@ public interface CommentRepository extends BlogRepository<CommentEntity, UUID> {
final UUID id); final UUID id);
int deleteById(UUID id); void deleteById(final UUID id);
} }

View File

@@ -2,6 +2,7 @@ package dev.enblng.api.services.impl;
import dev.enblng.api.dto.CommentTO; import dev.enblng.api.dto.CommentTO;
import dev.enblng.api.entities.CommentEntity; import dev.enblng.api.entities.CommentEntity;
import dev.enblng.api.mappers.CommentMapper;
import dev.enblng.api.repositories.CommentRepository; import dev.enblng.api.repositories.CommentRepository;
import dev.enblng.api.services.CommentService; import dev.enblng.api.services.CommentService;
import org.modelmapper.ModelMapper; import org.modelmapper.ModelMapper;
@@ -17,27 +18,31 @@ import java.util.stream.Collectors;
@Service @Service
public class CommentServiceImpl implements CommentService { public class CommentServiceImpl implements CommentService {
private final ModelMapper modelMapper;
private final CommentRepository commentRepository; private final CommentRepository commentRepository;
private final CommentMapper commentMapper;
public CommentServiceImpl(final ModelMapper modelMapper, final CommentRepository commentRepository) { public CommentServiceImpl(
this.modelMapper = modelMapper; final ModelMapper modelMapper,
final CommentRepository commentRepository,
final CommentMapper commentMapper
) {
this.commentMapper = commentMapper;
this.commentRepository = commentRepository; this.commentRepository = commentRepository;
} }
@Override @Override
@Transactional
public CommentTO save(final CommentTO comment) { public CommentTO save(final CommentTO comment) {
final CommentEntity entity = modelMapper.map(comment, CommentEntity.class); final CommentEntity entity = commentMapper.toEntity(comment);
entity.setId(UUID.randomUUID()); entity.setId(UUID.randomUUID());
return modelMapper.map(commentRepository.save(entity), CommentTO.CommentTOBuilder.class).build(); return commentMapper.toTransferObject(commentRepository.save(entity));
} }
@Override @Override
@Transactional @Transactional
public List<CommentTO> getByPostId(final UUID id, final Pageable pageable) { public List<CommentTO> getByPostId(final UUID id, final Pageable pageable) {
return commentRepository.findAllByPostIdOrderByCreationTime(id, pageable) return commentRepository.findAllByPostIdOrderByCreationTime(id, pageable)
.map(commentEntity -> modelMapper.map(commentEntity, .map(commentMapper::toTransferObject)
CommentTO.CommentTOBuilder.class).build())
.collect(Collectors.toList()); .collect(Collectors.toList());
} }

View File

@@ -2,49 +2,57 @@ package dev.enblng.api.services.impl;
import dev.enblng.api.dto.PostTO; import dev.enblng.api.dto.PostTO;
import dev.enblng.api.entities.PostEntity; import dev.enblng.api.entities.PostEntity;
import dev.enblng.api.mappers.PostMapper;
import dev.enblng.api.repositories.PostRepository; import dev.enblng.api.repositories.PostRepository;
import dev.enblng.api.services.PostService; import dev.enblng.api.services.PostService;
import lombok.extern.slf4j.Slf4j;
import org.modelmapper.ModelMapper; import org.modelmapper.ModelMapper;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import javax.persistence.EntityNotFoundException; import javax.persistence.EntityNotFoundException;
import javax.transaction.Transactional;
import java.time.ZonedDateTime; import java.time.ZonedDateTime;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@Service @Service
@Slf4j
public class PostServiceImpl implements PostService { public class PostServiceImpl implements PostService {
private final PostRepository postRepository; private final PostRepository postRepository;
private final ModelMapper modelMapper; private final PostMapper postMapper;
@Autowired @Autowired
public PostServiceImpl(final ModelMapper modelMapper, final PostRepository postRepository) { public PostServiceImpl(final ModelMapper modelMapper,
this.modelMapper = modelMapper; final PostRepository postRepository,
final PostMapper postMapper) {
this.postRepository = postRepository; this.postRepository = postRepository;
this.postMapper = postMapper;
} }
@Override @Override
@Transactional
public PostTO save(final PostTO post) { public PostTO save(final PostTO post) {
final PostEntity entity = modelMapper.map(post, PostEntity.class); final PostEntity entity = postMapper.toEntity(post);
log.debug(post.toString());
entity.setId(UUID.randomUUID()); entity.setId(UUID.randomUUID());
return modelMapper.map(postRepository.save(entity), PostTO.PostTOBuilder.class) final PostEntity savedEntity = postRepository.save(entity);
.build(); return postMapper.toTransferObject(savedEntity);
} }
@Override @Override
public PostTO getById(final UUID id) { public PostTO getById(final UUID id) {
final PostEntity entity = postRepository.getById(id) final PostEntity entity = postRepository.getById(id)
.orElseThrow(EntityNotFoundException::new); .orElseThrow(EntityNotFoundException::new);
return modelMapper.map(entity, PostTO.PostTOBuilder.class).build(); return postMapper.toTransferObject(entity);
} }
@Override @Override
public List<PostTO> findAll(final Pageable pageable) { public List<PostTO> findAll(final Pageable pageable) {
return postRepository.findAll(pageable).get() return postRepository.findAll(pageable).get()
.map(postEntity -> modelMapper.map(postEntity, PostTO.PostTOBuilder.class).build()) .map(postMapper::toTransferObject)
.collect(Collectors.toList()); .collect(Collectors.toList());
} }