rest 개발

This commit is contained in:
sejoung kim
2021-06-23 17:11:19 +09:00
parent 84cf3d1577
commit 99e5ea3f4f
18 changed files with 275 additions and 13 deletions

View File

@@ -8,7 +8,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import io.github.sejoung.product.persistence.repository.ProductRepository;
import io.github.sejoung.product.persistence.util.TestUtil;
import io.github.sejoung.product.persistence.util.JpaTestUtil;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@@ -27,7 +27,7 @@ class SaveRoundProductServiceTest {
@Test
void saveRoundProduct() {
var actual = service.saveRoundProduct(TestUtil.defaultRoundProduct());
var actual = service.saveRoundProduct(JpaTestUtil.defaultRoundProduct());
log.debug("{}",actual);
assertThat(actual.getProductId()).isNotNull();

View File

@@ -4,7 +4,7 @@ import io.github.sejoung.product.entities.Category;
import io.github.sejoung.product.entities.Product;
import io.github.sejoung.product.entities.RoundProduct;
public interface TestUtil {
public interface JpaTestUtil {
static RoundProduct defaultRoundProduct() {
return new RoundProduct(null, 1L, Category.ProductType.ROUND, Product.ProductStatus.CREATE, "회차권", 2);
}

View File

@@ -0,0 +1,5 @@
package io.github.sejoung.product.rest.constants;
public enum ProductStatus {
CREATE, DELETE, TEMPORARY, PERMANENTLY_DELETE
}

View File

@@ -0,0 +1,5 @@
package io.github.sejoung.product.rest.constants;
public enum ProductType {
ROUND, PERIOD
}

View File

@@ -0,0 +1,32 @@
package io.github.sejoung.product.rest.controller;
import javax.validation.Valid;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import io.github.sejoung.product.rest.dto.SaveRoundProduct;
import io.github.sejoung.product.rest.mapper.SaveRoundProductMapper;
import io.github.sejoung.product.usecases.port.in.SaveRoundProductInUseCase;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@RestController
public class SaveRoundProductController {
private final SaveRoundProductInUseCase saveRoundProductInUseCase;
public SaveRoundProductController(
SaveRoundProductInUseCase saveRoundProductInUseCase) {
this.saveRoundProductInUseCase = saveRoundProductInUseCase;
}
@PostMapping("/product/round")
public SaveRoundProduct roundSave(
@Valid @RequestBody SaveRoundProduct dto) {
log.debug("{}", dto);
var command = saveRoundProductInUseCase.save(SaveRoundProductMapper.toEntity(dto));
return SaveRoundProductMapper.toDto(command);
}
}

View File

@@ -0,0 +1,46 @@
package io.github.sejoung.product.rest.dto;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import io.github.sejoung.product.rest.constants.ProductStatus;
import io.github.sejoung.product.rest.constants.ProductType;
import lombok.Builder;
import lombok.Getter;
import lombok.ToString;
@Getter
@ToString
public class SaveRoundProduct {
private final Long productId;
@NotNull
private final Long categoryId;
@NotNull
private final ProductType productType;
@NotNull
private final ProductStatus status;
@NotEmpty
private final String productName;
@Min(1)
@Max(99)
private final Integer count;
@Builder
private SaveRoundProduct(Long productId, Long categoryId,
ProductType productType, ProductStatus status, String productName, Integer count) {
this.productId = productId;
this.categoryId = categoryId;
this.productType = productType;
this.status = status;
this.productName = productName;
this.count = count;
}
}

View File

@@ -0,0 +1,28 @@
package io.github.sejoung.product.rest.mapper;
import io.github.sejoung.product.entities.Product;
import io.github.sejoung.product.rest.constants.ProductType;
import io.github.sejoung.product.rest.dto.SaveRoundProduct;
import io.github.sejoung.product.usecases.port.in.SaveRoundProductInUseCase;
public interface SaveRoundProductMapper {
static SaveRoundProductInUseCase.SaveRoundProductCommand toEntity(SaveRoundProduct dto) {
return SaveRoundProductInUseCase.SaveRoundProductCommand.builder()
.productName(dto.getProductName())
.categoryId(dto.getCategoryId())
.productId(dto.getProductId())
.status(Product.ProductStatus.valueOf(dto.getStatus().name()))
.count(dto.getCount())
.build();
}
static SaveRoundProduct toDto(SaveRoundProductInUseCase.SaveRoundProductCommand command) {
return SaveRoundProduct.builder()
.productType(ProductType.valueOf(command.getProductType().name()))
.productId(command.getProductId())
.productName(command.getProductName())
.categoryId(command.getCategoryId())
.count(command.getCount())
.build();
}
}

View File

@@ -0,0 +1,2 @@
server:
port: 8080

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<include resource="org/springframework/boot/logging/logback/defaults.xml" />
<include resource="org/springframework/boot/logging/logback/console-appender.xml" />
<logger name="org.springframework" level="INFO" />
<logger name="io.github.sejoung" level="DEBUG" />
<root level="INFO">
<appender-ref ref="CONSOLE" />
</root>
</configuration>

View File

@@ -0,0 +1,15 @@
package io.github.sejoung.product.rest;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import io.github.sejoung.product.rest.util.SaveRoundProductInUseCaseStub;
import io.github.sejoung.product.usecases.port.in.SaveRoundProductInUseCase;
@SpringBootApplication
public class TestApplication {
@Bean
public SaveRoundProductInUseCase saveRoundProductInUseCase() {
return new SaveRoundProductInUseCaseStub();
}
}

View File

@@ -0,0 +1,18 @@
package io.github.sejoung.product.rest.constants;
import static org.assertj.core.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
import io.github.sejoung.product.entities.Category;
class ProductTypeTest {
@Test
void valueOf() {
var actal = ProductType.valueOf(Category.ProductType.ROUND.name());
assertThat(actal).isNotNull();
}
}

View File

@@ -0,0 +1,46 @@
package io.github.sejoung.product.rest.controller;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.BDDMockito.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.github.sejoung.product.rest.util.RestTestUtil;
import io.github.sejoung.product.rest.util.SaveRoundProductInUseCaseStub;
import io.github.sejoung.product.usecases.port.in.SaveRoundProductInUseCase;
@WebMvcTest(controllers = SaveRoundProductController.class)
class SaveRoundProductControllerTest {
private static final String URL = "/product/round";
@Autowired
private MockMvc mockMvc;
@DisplayName("회차권 API 테스트")
@Test
void saveRoundProduct() throws Exception {
var objectMapper = new ObjectMapper();
var json = objectMapper.writeValueAsString(RestTestUtil.defaultSaveRoundProductCommand(null));
mockMvc.perform(post(URL)
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)
.content(json))
.andDo(print())
.andExpect(status().isOk());
}
}

View File

@@ -0,0 +1,18 @@
package io.github.sejoung.product.rest.util;
import io.github.sejoung.product.entities.Product;
import io.github.sejoung.product.usecases.port.in.SaveRoundProductInUseCase;
public interface RestTestUtil {
static SaveRoundProductInUseCase.SaveRoundProductCommand defaultSaveRoundProductCommand(Long productId) {
return SaveRoundProductInUseCase.SaveRoundProductCommand.builder()
.count(1)
.status(Product.ProductStatus.CREATE)
.productId(productId)
.productName("회차권")
.categoryId(1L)
.build();
}
}

View File

@@ -0,0 +1,19 @@
package io.github.sejoung.product.rest.util;
import io.github.sejoung.product.usecases.port.in.SaveRoundProductInUseCase;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class SaveRoundProductInUseCaseStub implements SaveRoundProductInUseCase {
@Override
public SaveRoundProductCommand save(SaveRoundProductCommand command) {
log.debug("{}", command);
return SaveRoundProductCommand.builder()
.categoryId(command.getCategoryId())
.productName(command.getProductName())
.productId(1L)
.status(command.getStatus())
.count(command.getCount())
.build();
}
}

View File

@@ -1,14 +1,20 @@
package io.github.sejoung.product.mapper;
import io.github.sejoung.product.entities.Product;
import io.github.sejoung.product.entities.RoundProduct;
import io.github.sejoung.product.usecases.port.in.SaveRoundProductInUseCase;
public interface SaveRoundProductCommandMapper {
static SaveRoundProductInUseCase.SaveRoundProductCommand toDto(RoundProduct roundProduct) {
return new SaveRoundProductInUseCase.SaveRoundProductCommand(roundProduct.getProductId(),
roundProduct.getCategory().getCategoryId(), roundProduct.getCategory().getProductType(),
roundProduct.getStatus(), roundProduct.getProductName(), roundProduct.getCount());
return SaveRoundProductInUseCase.SaveRoundProductCommand.builder()
.productName(roundProduct.getProductName())
.productId(roundProduct.getProductId())
.status(
Product.ProductStatus.valueOf(roundProduct.getStatus().name()))
.categoryId(roundProduct.getCategory().getCategoryId())
.count(roundProduct.getCount())
.build();
}
static RoundProduct toEntity(SaveRoundProductInUseCase.SaveRoundProductCommand command) {

View File

@@ -9,14 +9,17 @@ import io.github.sejoung.product.entities.Category;
import io.github.sejoung.product.entities.Product;
import io.github.sejoung.product.validating.SelfValidating;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;
public interface SaveRoundProductInUseCase {
SaveRoundProductCommand save(SaveRoundProductCommand command);
@Getter
@ToString
@EqualsAndHashCode(callSuper = false)
final class SaveRoundProductCommand extends SelfValidating<SaveRoundProductCommand> {
@@ -38,11 +41,12 @@ public interface SaveRoundProductInUseCase {
@Max(99)
private final Integer count;
public SaveRoundProductCommand(Long productId, Long categoryId, Category.ProductType productType,
@Builder
private SaveRoundProductCommand(Long productId, Long categoryId,
Product.ProductStatus status, String productName, Integer count) {
this.productId = productId;
this.categoryId = categoryId;
this.productType = productType;
this.productType = Category.ProductType.ROUND;
this.status = status;
this.productName = productName;
this.count = count;

View File

@@ -10,7 +10,6 @@ import org.junit.jupiter.api.Test;
import io.github.sejoung.product.entities.Category;
import io.github.sejoung.product.entities.Product;
class SaveRoundProductInUseCaseTest {
@DisplayName("Commend 회차권 count는 99 이하여야 합니다")
@@ -18,8 +17,13 @@ class SaveRoundProductInUseCaseTest {
void validationSaveRoundProductCommand() {
Throwable exception = assertThrows(ConstraintViolationException.class, () -> {
var command = new SaveRoundProductInUseCase.SaveRoundProductCommand(null, 1L, Category.ProductType.ROUND,
Product.ProductStatus.CREATE, "2회권", 999);
SaveRoundProductInUseCase.SaveRoundProductCommand.builder()
.categoryId(1L)
.status(Product.ProductStatus.CREATE)
.productName("안녕")
.count(999)
.build();
});
assertEquals("count: 99 이하여야 합니다", exception.getMessage());

View File

@@ -22,8 +22,12 @@ class SaveRoundProductServiceTest {
@DisplayName("회차권 저장")
@Test
void saveTest() {
var command = new SaveRoundProductInUseCase.SaveRoundProductCommand(null, 1L, Category.ProductType.ROUND,
Product.ProductStatus.CREATE, "2회권", 2);
var command = SaveRoundProductInUseCase.SaveRoundProductCommand.builder()
.productName("")
.count(99)
.categoryId(1L)
.status(Product.ProductStatus.CREATE)
.build();
var actual = service.save(command);
Assertions.assertEquals(99, actual.getProductId());
}