From 5ff8f3625607af220489a1413e2374eec0b90593 Mon Sep 17 00:00:00 2001 From: HiiWee Date: Wed, 15 Mar 2023 23:01:12 +0900 Subject: [PATCH 01/16] =?UTF-8?q?feat:=20=EC=83=81=ED=92=88=20=EB=93=B1?= =?UTF-8?q?=EB=A1=9D=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../product/controller/ProductController.java | 32 ++++++++++++++++ .../jscode/spring/product/domain/Product.java | 27 +++++++++++++ .../spring/product/dto/NewProductRequest.java | 24 ++++++++++++ .../product/repository/ProductRepository.java | 38 +++++++++++++++++++ .../product/service/ProductService.java | 22 +++++++++++ .../{ => test}/controller/TestController.java | 2 +- .../spring/product/domain/ProductTest.java | 17 +++++++++ .../repository/ProductRepositoryTest.java | 24 ++++++++++++ .../product/service/ProductServiceTest.java | 36 ++++++++++++++++++ 9 files changed, 221 insertions(+), 1 deletion(-) create mode 100644 spring/src/main/java/com/jscode/spring/product/controller/ProductController.java create mode 100644 spring/src/main/java/com/jscode/spring/product/domain/Product.java create mode 100644 spring/src/main/java/com/jscode/spring/product/dto/NewProductRequest.java create mode 100644 spring/src/main/java/com/jscode/spring/product/repository/ProductRepository.java create mode 100644 spring/src/main/java/com/jscode/spring/product/service/ProductService.java rename spring/src/main/java/com/jscode/spring/{ => test}/controller/TestController.java (94%) create mode 100644 spring/src/test/java/com/jscode/spring/product/domain/ProductTest.java create mode 100644 spring/src/test/java/com/jscode/spring/product/repository/ProductRepositoryTest.java create mode 100644 spring/src/test/java/com/jscode/spring/product/service/ProductServiceTest.java diff --git a/spring/src/main/java/com/jscode/spring/product/controller/ProductController.java b/spring/src/main/java/com/jscode/spring/product/controller/ProductController.java new file mode 100644 index 0000000..f1b7dc8 --- /dev/null +++ b/spring/src/main/java/com/jscode/spring/product/controller/ProductController.java @@ -0,0 +1,32 @@ +package com.jscode.spring.product.controller; + +import com.jscode.spring.product.dto.NewProductRequest; +import com.jscode.spring.product.service.ProductService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@Slf4j +@RestController +@RequestMapping("/api") +public class ProductController { + + private final ProductService productService; + + public ProductController(final ProductService productService) { + this.productService = productService; + } + + /** + * (연습문제) 상품 등록 api 역할 구분하기 + */ + @PostMapping("/products") + public String saveProduct(@RequestBody final NewProductRequest newProductRequest) { + log.info("call"); + Long generatedId = productService.saveProduct(newProductRequest); + return String.valueOf(generatedId); + } + +} diff --git a/spring/src/main/java/com/jscode/spring/product/domain/Product.java b/spring/src/main/java/com/jscode/spring/product/domain/Product.java new file mode 100644 index 0000000..19b5f90 --- /dev/null +++ b/spring/src/main/java/com/jscode/spring/product/domain/Product.java @@ -0,0 +1,27 @@ +package com.jscode.spring.product.domain; + +import java.util.Objects; +import lombok.Getter; + +@Getter +public class Product { + + private Long id; + private String name; + private int price; + + public Product(final Long id, final String name, final int price) { + this.id = id; + this.name = name; + this.price = price; + } + + public void generateId(final Long id) { + this.id = id; + } + + public boolean contains(final Long id) { + return Objects.equals(this.id, id); + } + +} diff --git a/spring/src/main/java/com/jscode/spring/product/dto/NewProductRequest.java b/spring/src/main/java/com/jscode/spring/product/dto/NewProductRequest.java new file mode 100644 index 0000000..3bc1ec3 --- /dev/null +++ b/spring/src/main/java/com/jscode/spring/product/dto/NewProductRequest.java @@ -0,0 +1,24 @@ +package com.jscode.spring.product.dto; + +import com.jscode.spring.product.domain.Product; +import lombok.Getter; + +@Getter +public class NewProductRequest { + + private String name; + private int price; + + public NewProductRequest() { + } + + public NewProductRequest(final String name, final int price) { + this.name = name; + this.price = price; + } + + public Product toEntity() { + return new Product(null, name, price); + } + +} diff --git a/spring/src/main/java/com/jscode/spring/product/repository/ProductRepository.java b/spring/src/main/java/com/jscode/spring/product/repository/ProductRepository.java new file mode 100644 index 0000000..4648ac1 --- /dev/null +++ b/spring/src/main/java/com/jscode/spring/product/repository/ProductRepository.java @@ -0,0 +1,38 @@ +package com.jscode.spring.product.repository; + +import com.jscode.spring.product.domain.Product; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import org.springframework.stereotype.Repository; + +@Repository +public class ProductRepository { + + private static final List store = new ArrayList<>(); + private static Long id = 3L; + + static { + store.add(new Product(1L, "컴퓨터", 3_000_000)); + store.add(new Product(2L, "키보드", 100_000)); + store.add(new Product(3L, "마우스", 50_000)); + } + + public List findAll() { + return Collections.unmodifiableList(store); + } + + public Product findById(final Long id) { + return store.stream() + .filter(product -> product.contains(id)) + .findAny() + .orElseThrow(() -> new IllegalArgumentException("존재하지 않는 상품입니다.")); + } + + public Long save(final Product product) { + product.generateId(++id); + store.add(product); + return product.getId(); + } + +} diff --git a/spring/src/main/java/com/jscode/spring/product/service/ProductService.java b/spring/src/main/java/com/jscode/spring/product/service/ProductService.java new file mode 100644 index 0000000..18abd4e --- /dev/null +++ b/spring/src/main/java/com/jscode/spring/product/service/ProductService.java @@ -0,0 +1,22 @@ +package com.jscode.spring.product.service; + +import com.jscode.spring.product.domain.Product; +import com.jscode.spring.product.dto.NewProductRequest; +import com.jscode.spring.product.repository.ProductRepository; +import org.springframework.stereotype.Service; + +@Service +public class ProductService { + + private final ProductRepository productRepository; + + public ProductService(final ProductRepository productRepository) { + this.productRepository = productRepository; + } + + public Long saveProduct(final NewProductRequest newProductRequest) { + Product product = newProductRequest.toEntity(); + return productRepository.save(product); + } + +} diff --git a/spring/src/main/java/com/jscode/spring/controller/TestController.java b/spring/src/main/java/com/jscode/spring/test/controller/TestController.java similarity index 94% rename from spring/src/main/java/com/jscode/spring/controller/TestController.java rename to spring/src/main/java/com/jscode/spring/test/controller/TestController.java index 4626f3a..449ae18 100644 --- a/spring/src/main/java/com/jscode/spring/controller/TestController.java +++ b/spring/src/main/java/com/jscode/spring/test/controller/TestController.java @@ -1,4 +1,4 @@ -package com.jscode.spring.controller; +package com.jscode.spring.test.controller; import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.GetMapping; diff --git a/spring/src/test/java/com/jscode/spring/product/domain/ProductTest.java b/spring/src/test/java/com/jscode/spring/product/domain/ProductTest.java new file mode 100644 index 0000000..2b3b45b --- /dev/null +++ b/spring/src/test/java/com/jscode/spring/product/domain/ProductTest.java @@ -0,0 +1,17 @@ +package com.jscode.spring.product.domain; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class ProductTest { + + @Test + @DisplayName("동일 상품 id 확인") + void contains() { + Product product = new Product(1L, "test", 3000); + + assertThat(product.contains(1L)).isTrue(); + } +} \ No newline at end of file diff --git a/spring/src/test/java/com/jscode/spring/product/repository/ProductRepositoryTest.java b/spring/src/test/java/com/jscode/spring/product/repository/ProductRepositoryTest.java new file mode 100644 index 0000000..1d5e96c --- /dev/null +++ b/spring/src/test/java/com/jscode/spring/product/repository/ProductRepositoryTest.java @@ -0,0 +1,24 @@ +package com.jscode.spring.product.repository; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.jscode.spring.product.domain.Product; +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class ProductRepositoryTest { + + ProductRepository productRepository; + + @BeforeEach + void setUp() { + productRepository = new ProductRepository(); + } + + @Test + void findAll() { + List products = productRepository.findAll(); + assertThat(products.size()).isEqualTo(3); + } +} \ No newline at end of file diff --git a/spring/src/test/java/com/jscode/spring/product/service/ProductServiceTest.java b/spring/src/test/java/com/jscode/spring/product/service/ProductServiceTest.java new file mode 100644 index 0000000..4ee27d0 --- /dev/null +++ b/spring/src/test/java/com/jscode/spring/product/service/ProductServiceTest.java @@ -0,0 +1,36 @@ +package com.jscode.spring.product.service; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.jscode.spring.product.domain.Product; +import com.jscode.spring.product.dto.NewProductRequest; +import com.jscode.spring.product.repository.ProductRepository; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class ProductServiceTest { + + @Autowired + ProductService productService; + + @Autowired + ProductRepository productRepository; + + @Test + @DisplayName("상품 저장 성공 테스트") + void saveProduct() { + productService.saveProduct(new NewProductRequest("test", 3000)); + Product product = productRepository.findById(4L); + + Assertions.assertAll( + () -> assertThat(product.getName()).isEqualTo("test"), + () -> assertThat(product.getPrice()).isEqualTo(3000) + ); + + } + +} \ No newline at end of file From 550e0f3c4e27dd6f582df88c23609206b6907545 Mon Sep 17 00:00:00 2001 From: HiiWee Date: Thu, 16 Mar 2023 01:15:18 +0900 Subject: [PATCH 02/16] =?UTF-8?q?feat:=20=EB=8B=AC=EB=9F=AC=20=ED=99=98?= =?UTF-8?q?=EC=9C=A8=20=EB=B3=80=ED=99=98=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- spring/.gitignore | 2 + .../exchange/config/ExchangeYamlRead.java | 18 +++++++++ .../config/YamlPropertySourceFactory.java | 24 ++++++++++++ .../spring/exchange/domain/ExchangeRates.java | 21 ++++++++++ .../service/ExchangeRatesService.java | 39 +++++++++++++++++++ .../src/main/resources/application.properties | 1 - spring/src/main/resources/application.yml | 0 .../config/ExchangesYamlReadTest.java | 18 +++++++++ .../service/DollarRateServiceTest.java | 17 ++++++++ 9 files changed, 139 insertions(+), 1 deletion(-) create mode 100644 spring/.gitignore create mode 100644 spring/src/main/java/com/jscode/spring/exchange/config/ExchangeYamlRead.java create mode 100644 spring/src/main/java/com/jscode/spring/exchange/config/YamlPropertySourceFactory.java create mode 100644 spring/src/main/java/com/jscode/spring/exchange/domain/ExchangeRates.java create mode 100644 spring/src/main/java/com/jscode/spring/exchange/service/ExchangeRatesService.java delete mode 100644 spring/src/main/resources/application.properties create mode 100644 spring/src/main/resources/application.yml create mode 100644 spring/src/test/java/com/jscode/spring/exchange/config/ExchangesYamlReadTest.java create mode 100644 spring/src/test/java/com/jscode/spring/exchange/service/DollarRateServiceTest.java diff --git a/spring/.gitignore b/spring/.gitignore new file mode 100644 index 0000000..de5aa0b --- /dev/null +++ b/spring/.gitignore @@ -0,0 +1,2 @@ +## api key +exchanges.yml \ No newline at end of file diff --git a/spring/src/main/java/com/jscode/spring/exchange/config/ExchangeYamlRead.java b/spring/src/main/java/com/jscode/spring/exchange/config/ExchangeYamlRead.java new file mode 100644 index 0000000..a487636 --- /dev/null +++ b/spring/src/main/java/com/jscode/spring/exchange/config/ExchangeYamlRead.java @@ -0,0 +1,18 @@ +package com.jscode.spring.exchange.config; + +import lombok.Getter; +import lombok.Setter; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.PropertySource; + +@Configuration +@PropertySource(value = "classpath:exchanges.yml", factory = YamlPropertySourceFactory.class) +@ConfigurationProperties(prefix = "apilayer") +@Getter +@Setter +public class ExchangeYamlRead { + + private String key; + +} diff --git a/spring/src/main/java/com/jscode/spring/exchange/config/YamlPropertySourceFactory.java b/spring/src/main/java/com/jscode/spring/exchange/config/YamlPropertySourceFactory.java new file mode 100644 index 0000000..190b435 --- /dev/null +++ b/spring/src/main/java/com/jscode/spring/exchange/config/YamlPropertySourceFactory.java @@ -0,0 +1,24 @@ +package com.jscode.spring.exchange.config; + +import java.util.Properties; +import org.springframework.beans.factory.config.YamlPropertiesFactoryBean; +import org.springframework.core.env.PropertiesPropertySource; +import org.springframework.core.env.PropertySource; +import org.springframework.core.io.support.EncodedResource; +import org.springframework.core.io.support.PropertySourceFactory; + +public class YamlPropertySourceFactory implements PropertySourceFactory { + + @Override + public PropertySource createPropertySource(final String name, final EncodedResource resource) { + YamlPropertiesFactoryBean factoryBean = new YamlPropertiesFactoryBean(); + factoryBean.setResources(resource.getResource()); + factoryBean.afterPropertiesSet(); + Properties properties = factoryBean.getObject(); + if (name != null) { + return new PropertiesPropertySource(name, properties); + } + return new PropertiesPropertySource(resource.getResource().getFilename(), properties); + } + +} diff --git a/spring/src/main/java/com/jscode/spring/exchange/domain/ExchangeRates.java b/spring/src/main/java/com/jscode/spring/exchange/domain/ExchangeRates.java new file mode 100644 index 0000000..35e5afa --- /dev/null +++ b/spring/src/main/java/com/jscode/spring/exchange/domain/ExchangeRates.java @@ -0,0 +1,21 @@ +package com.jscode.spring.exchange.domain; + +import java.time.LocalDate; +import java.util.Map; +import lombok.Getter; + +@Getter +public class ExchangeRates { + + private String base; + private LocalDate date; + private Map rates; + + public ExchangeRates() { + } + + public double findRates(final String countryName) { + return rates.get(countryName); + } + +} diff --git a/spring/src/main/java/com/jscode/spring/exchange/service/ExchangeRatesService.java b/spring/src/main/java/com/jscode/spring/exchange/service/ExchangeRatesService.java new file mode 100644 index 0000000..9a7623b --- /dev/null +++ b/spring/src/main/java/com/jscode/spring/exchange/service/ExchangeRatesService.java @@ -0,0 +1,39 @@ +package com.jscode.spring.exchange.service; + +import com.jscode.spring.exchange.config.ExchangeYamlRead; +import com.jscode.spring.exchange.domain.ExchangeRates; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; + +@Service +public class ExchangeRatesService { + + private final ExchangeYamlRead exchangeYamlRead; + + public ExchangeRatesService(final ExchangeYamlRead exchangeYamlRead) { + this.exchangeYamlRead = exchangeYamlRead; + } + + public double convertKrwToUsd(final int krw) { + RestTemplate template = new RestTemplate(); + ResponseEntity exchange = template.exchange( + "https://api.apilayer.com/exchangerates_data/latest?symbols=KRW&base=USD", + HttpMethod.GET, + new HttpEntity<>(generateHeader("apikey")), + ExchangeRates.class + ); + return krw / exchange.getBody() + .findRates("KRW"); + } + + private HttpHeaders generateHeader(final String header) { + HttpHeaders httpHeaders = new HttpHeaders(); + httpHeaders.set(header, exchangeYamlRead.getKey()); + return httpHeaders; + } + +} diff --git a/spring/src/main/resources/application.properties b/spring/src/main/resources/application.properties deleted file mode 100644 index 8b13789..0000000 --- a/spring/src/main/resources/application.properties +++ /dev/null @@ -1 +0,0 @@ - diff --git a/spring/src/main/resources/application.yml b/spring/src/main/resources/application.yml new file mode 100644 index 0000000..e69de29 diff --git a/spring/src/test/java/com/jscode/spring/exchange/config/ExchangesYamlReadTest.java b/spring/src/test/java/com/jscode/spring/exchange/config/ExchangesYamlReadTest.java new file mode 100644 index 0000000..69c5567 --- /dev/null +++ b/spring/src/test/java/com/jscode/spring/exchange/config/ExchangesYamlReadTest.java @@ -0,0 +1,18 @@ +package com.jscode.spring.exchange.config; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class ExchangesYamlReadTest { + + @Autowired + ExchangeYamlRead exchangesYamlRead; + + @Test + void getKey() { + Assertions.assertThat(exchangesYamlRead.getKey()).isNotNull(); + } +} \ No newline at end of file diff --git a/spring/src/test/java/com/jscode/spring/exchange/service/DollarRateServiceTest.java b/spring/src/test/java/com/jscode/spring/exchange/service/DollarRateServiceTest.java new file mode 100644 index 0000000..0dc53ec --- /dev/null +++ b/spring/src/test/java/com/jscode/spring/exchange/service/DollarRateServiceTest.java @@ -0,0 +1,17 @@ +package com.jscode.spring.exchange.service; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class DollarRateServiceTest { + + @Autowired + ExchangeRatesService exchangeRatesService; + + @Test + void convertKrwToUsd() { + System.out.println(exchangeRatesService.convertKrwToUsd(10000)); + } +} \ No newline at end of file From 92fe0eb341dc295a802505ca5bc1b478ed65710d Mon Sep 17 00:00:00 2001 From: HiiWee Date: Thu, 16 Mar 2023 01:30:45 +0900 Subject: [PATCH 03/16] =?UTF-8?q?feat:=20=EB=8B=AC=EB=9F=AC=20=EA=B0=80?= =?UTF-8?q?=EA=B2=A9=EC=9D=84=20=EB=B0=98=ED=99=98=ED=95=98=EB=8A=94=20?= =?UTF-8?q?=EB=8B=A8=EC=9D=BC=20=EC=83=81=ED=92=88=20=EC=A1=B0=ED=9A=8C=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../product/controller/ProductController.java | 14 +++++++++-- .../spring/product/dto/ProductResponse.java | 23 +++++++++++++++++++ .../product/service/ProductService.java | 12 +++++++++- .../spring/product/domain/ProductTest.java | 1 + .../repository/ProductRepositoryTest.java | 18 +++++++++++++++ 5 files changed, 65 insertions(+), 3 deletions(-) create mode 100644 spring/src/main/java/com/jscode/spring/product/dto/ProductResponse.java diff --git a/spring/src/main/java/com/jscode/spring/product/controller/ProductController.java b/spring/src/main/java/com/jscode/spring/product/controller/ProductController.java index f1b7dc8..2f5dcda 100644 --- a/spring/src/main/java/com/jscode/spring/product/controller/ProductController.java +++ b/spring/src/main/java/com/jscode/spring/product/controller/ProductController.java @@ -1,8 +1,11 @@ package com.jscode.spring.product.controller; import com.jscode.spring.product.dto.NewProductRequest; +import com.jscode.spring.product.dto.ProductResponse; import com.jscode.spring.product.service.ProductService; import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; @@ -20,13 +23,20 @@ public ProductController(final ProductService productService) { } /** - * (연습문제) 상품 등록 api 역할 구분하기 + * (연습문제) 상품 등록 api */ @PostMapping("/products") public String saveProduct(@RequestBody final NewProductRequest newProductRequest) { - log.info("call"); Long generatedId = productService.saveProduct(newProductRequest); return String.valueOf(generatedId); } + /** + * (연습문제) 상품 조회 api + */ + @GetMapping("/products/{productId}") + public ProductResponse product(@PathVariable final Long productId) { + return productService.findProductUsdPrice(productId); + } + } diff --git a/spring/src/main/java/com/jscode/spring/product/dto/ProductResponse.java b/spring/src/main/java/com/jscode/spring/product/dto/ProductResponse.java new file mode 100644 index 0000000..b7940a9 --- /dev/null +++ b/spring/src/main/java/com/jscode/spring/product/dto/ProductResponse.java @@ -0,0 +1,23 @@ +package com.jscode.spring.product.dto; + +import com.jscode.spring.product.domain.Product; +import lombok.Getter; + +@Getter +public class ProductResponse { + + private Long id; + private String name; + private double price; + + private ProductResponse(final Long id, final String name, final double price) { + this.id = id; + this.name = name; + this.price = price; + } + + public static ProductResponse createUsdPriceResponse(final Product product, final double usdPrice) { + return new ProductResponse(product.getId(), product.getName(), usdPrice); + } + +} diff --git a/spring/src/main/java/com/jscode/spring/product/service/ProductService.java b/spring/src/main/java/com/jscode/spring/product/service/ProductService.java index 18abd4e..581c6e5 100644 --- a/spring/src/main/java/com/jscode/spring/product/service/ProductService.java +++ b/spring/src/main/java/com/jscode/spring/product/service/ProductService.java @@ -1,17 +1,22 @@ package com.jscode.spring.product.service; +import com.jscode.spring.exchange.service.ExchangeRatesService; import com.jscode.spring.product.domain.Product; import com.jscode.spring.product.dto.NewProductRequest; +import com.jscode.spring.product.dto.ProductResponse; import com.jscode.spring.product.repository.ProductRepository; import org.springframework.stereotype.Service; @Service public class ProductService { + private final ExchangeRatesService exchangeRatesService; + private final ProductRepository productRepository; - public ProductService(final ProductRepository productRepository) { + public ProductService(final ProductRepository productRepository, final ExchangeRatesService exchangeRatesService) { this.productRepository = productRepository; + this.exchangeRatesService = exchangeRatesService; } public Long saveProduct(final NewProductRequest newProductRequest) { @@ -19,4 +24,9 @@ public Long saveProduct(final NewProductRequest newProductRequest) { return productRepository.save(product); } + public ProductResponse findProductUsdPrice(final Long productId) { + Product product = productRepository.findById(productId); + double usdPrice = exchangeRatesService.convertKrwToUsd(product.getPrice()); + return ProductResponse.createUsdPriceResponse(product, usdPrice); + } } diff --git a/spring/src/test/java/com/jscode/spring/product/domain/ProductTest.java b/spring/src/test/java/com/jscode/spring/product/domain/ProductTest.java index 2b3b45b..64d36eb 100644 --- a/spring/src/test/java/com/jscode/spring/product/domain/ProductTest.java +++ b/spring/src/test/java/com/jscode/spring/product/domain/ProductTest.java @@ -14,4 +14,5 @@ void contains() { assertThat(product.contains(1L)).isTrue(); } + } \ No newline at end of file diff --git a/spring/src/test/java/com/jscode/spring/product/repository/ProductRepositoryTest.java b/spring/src/test/java/com/jscode/spring/product/repository/ProductRepositoryTest.java index 1d5e96c..658aaf4 100644 --- a/spring/src/test/java/com/jscode/spring/product/repository/ProductRepositoryTest.java +++ b/spring/src/test/java/com/jscode/spring/product/repository/ProductRepositoryTest.java @@ -4,7 +4,9 @@ import com.jscode.spring.product.domain.Product; import java.util.List; +import org.assertj.core.api.Assertions; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; class ProductRepositoryTest { @@ -17,8 +19,24 @@ void setUp() { } @Test + @DisplayName("전체 상품 조회 테스트") void findAll() { List products = productRepository.findAll(); assertThat(products.size()).isEqualTo(3); } + + @Test + @DisplayName("특정 상품 조회 성공 테스트") + void findById_success() { + Product product = productRepository.findById(1L); + assertThat(product.getId()).isEqualTo(1L); + } + + @Test + @DisplayName("특정 상품 조회 실패시 예외 발생 테스트") + void findById_fail_withException() { + Assertions.assertThatThrownBy(() -> productRepository.findById(11111L)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("존재하지 않는 상품입니다."); + } } \ No newline at end of file From 76ca67e07dec4719fcdd4b1041c5aadf8ac41d22 Mon Sep 17 00:00:00 2001 From: HiiWee Date: Thu, 16 Mar 2023 01:53:12 +0900 Subject: [PATCH 04/16] =?UTF-8?q?feat:=20=EC=A0=84=EC=B2=B4=20=EC=83=81?= =?UTF-8?q?=ED=92=88=20=EC=A1=B0=ED=9A=8C=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../product/controller/ProductController.java | 10 +++++++- .../product/dto/ProductListResponse.java | 23 +++++++++++++++++++ .../spring/product/dto/ProductResponse.java | 5 +++- .../product/service/ProductService.java | 12 +++++++++- .../product/service/ProductServiceTest.java | 1 - 5 files changed, 47 insertions(+), 4 deletions(-) create mode 100644 spring/src/main/java/com/jscode/spring/product/dto/ProductListResponse.java diff --git a/spring/src/main/java/com/jscode/spring/product/controller/ProductController.java b/spring/src/main/java/com/jscode/spring/product/controller/ProductController.java index 2f5dcda..e665eaa 100644 --- a/spring/src/main/java/com/jscode/spring/product/controller/ProductController.java +++ b/spring/src/main/java/com/jscode/spring/product/controller/ProductController.java @@ -1,6 +1,7 @@ package com.jscode.spring.product.controller; import com.jscode.spring.product.dto.NewProductRequest; +import com.jscode.spring.product.dto.ProductListResponse; import com.jscode.spring.product.dto.ProductResponse; import com.jscode.spring.product.service.ProductService; import lombok.extern.slf4j.Slf4j; @@ -35,8 +36,15 @@ public String saveProduct(@RequestBody final NewProductRequest newProductRequest * (연습문제) 상품 조회 api */ @GetMapping("/products/{productId}") - public ProductResponse product(@PathVariable final Long productId) { + public ProductResponse findProduct(@PathVariable final Long productId) { return productService.findProductUsdPrice(productId); } + /** + * (연습문제) 전체 상품 조회 + */ + @GetMapping("/products") + public ProductListResponse findProducts() { + return productService.findAllProduct(); + } } diff --git a/spring/src/main/java/com/jscode/spring/product/dto/ProductListResponse.java b/spring/src/main/java/com/jscode/spring/product/dto/ProductListResponse.java new file mode 100644 index 0000000..25dc4b2 --- /dev/null +++ b/spring/src/main/java/com/jscode/spring/product/dto/ProductListResponse.java @@ -0,0 +1,23 @@ +package com.jscode.spring.product.dto; + +import com.jscode.spring.product.domain.Product; +import java.util.List; +import java.util.stream.Collectors; +import lombok.Getter; + +@Getter +public class ProductListResponse { + + private List productResponses; + + private ProductListResponse(final List productResponses) { + this.productResponses = productResponses; + } + + public static ProductListResponse createListFrom(final List products) { + return new ProductListResponse(products.stream() + .map(ProductResponse::createResponseFrom) + .collect(Collectors.toList()) + ); + } +} diff --git a/spring/src/main/java/com/jscode/spring/product/dto/ProductResponse.java b/spring/src/main/java/com/jscode/spring/product/dto/ProductResponse.java index b7940a9..82fd5ec 100644 --- a/spring/src/main/java/com/jscode/spring/product/dto/ProductResponse.java +++ b/spring/src/main/java/com/jscode/spring/product/dto/ProductResponse.java @@ -16,8 +16,11 @@ private ProductResponse(final Long id, final String name, final double price) { this.price = price; } - public static ProductResponse createUsdPriceResponse(final Product product, final double usdPrice) { + public static ProductResponse createUsdPriceResponseOf(final Product product, final double usdPrice) { return new ProductResponse(product.getId(), product.getName(), usdPrice); } + public static ProductResponse createResponseFrom(final Product product) { + return new ProductResponse(product.getId(), product.getName(), product.getPrice()); + } } diff --git a/spring/src/main/java/com/jscode/spring/product/service/ProductService.java b/spring/src/main/java/com/jscode/spring/product/service/ProductService.java index 581c6e5..122a016 100644 --- a/spring/src/main/java/com/jscode/spring/product/service/ProductService.java +++ b/spring/src/main/java/com/jscode/spring/product/service/ProductService.java @@ -3,8 +3,10 @@ import com.jscode.spring.exchange.service.ExchangeRatesService; import com.jscode.spring.product.domain.Product; import com.jscode.spring.product.dto.NewProductRequest; +import com.jscode.spring.product.dto.ProductListResponse; import com.jscode.spring.product.dto.ProductResponse; import com.jscode.spring.product.repository.ProductRepository; +import java.util.List; import org.springframework.stereotype.Service; @Service @@ -27,6 +29,14 @@ public Long saveProduct(final NewProductRequest newProductRequest) { public ProductResponse findProductUsdPrice(final Long productId) { Product product = productRepository.findById(productId); double usdPrice = exchangeRatesService.convertKrwToUsd(product.getPrice()); - return ProductResponse.createUsdPriceResponse(product, usdPrice); + return ProductResponse.createUsdPriceResponseOf(product, usdPrice); + } + + /** + * TODO: 테스트하기 어려움 + */ + public ProductListResponse findAllProduct() { + List products = productRepository.findAll(); + return ProductListResponse.createListFrom(products); } } diff --git a/spring/src/test/java/com/jscode/spring/product/service/ProductServiceTest.java b/spring/src/test/java/com/jscode/spring/product/service/ProductServiceTest.java index 4ee27d0..fc83a0b 100644 --- a/spring/src/test/java/com/jscode/spring/product/service/ProductServiceTest.java +++ b/spring/src/test/java/com/jscode/spring/product/service/ProductServiceTest.java @@ -30,7 +30,6 @@ void saveProduct() { () -> assertThat(product.getName()).isEqualTo("test"), () -> assertThat(product.getPrice()).isEqualTo(3000) ); - } } \ No newline at end of file From 701bc6d81aa21d7a7395198cae6001d4f47c1244 Mon Sep 17 00:00:00 2001 From: HiiWee Date: Thu, 16 Mar 2023 15:45:51 +0900 Subject: [PATCH 05/16] =?UTF-8?q?refactor:=20id=ED=86=B5=ED=95=9C=20?= =?UTF-8?q?=EC=83=81=ED=92=88=20=EC=A1=B0=ED=9A=8C=EC=8B=9C=20null=20?= =?UTF-8?q?=EB=B0=98=ED=99=98=20=ED=95=98=EB=8F=84=EB=A1=9D=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/jscode/spring/product/repository/ProductRepository.java | 2 +- .../jscode/spring/product/repository/ProductRepositoryTest.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/spring/src/main/java/com/jscode/spring/product/repository/ProductRepository.java b/spring/src/main/java/com/jscode/spring/product/repository/ProductRepository.java index 4648ac1..a53e305 100644 --- a/spring/src/main/java/com/jscode/spring/product/repository/ProductRepository.java +++ b/spring/src/main/java/com/jscode/spring/product/repository/ProductRepository.java @@ -26,7 +26,7 @@ public Product findById(final Long id) { return store.stream() .filter(product -> product.contains(id)) .findAny() - .orElseThrow(() -> new IllegalArgumentException("존재하지 않는 상품입니다.")); + .orElse(null); } public Long save(final Product product) { diff --git a/spring/src/test/java/com/jscode/spring/product/repository/ProductRepositoryTest.java b/spring/src/test/java/com/jscode/spring/product/repository/ProductRepositoryTest.java index 658aaf4..b832d0c 100644 --- a/spring/src/test/java/com/jscode/spring/product/repository/ProductRepositoryTest.java +++ b/spring/src/test/java/com/jscode/spring/product/repository/ProductRepositoryTest.java @@ -22,7 +22,7 @@ void setUp() { @DisplayName("전체 상품 조회 테스트") void findAll() { List products = productRepository.findAll(); - assertThat(products.size()).isEqualTo(3); + assertThat(products.size()).isNotZero(); } @Test From a9282566fb8454216ed836113f30f289eed81528 Mon Sep 17 00:00:00 2001 From: HiiWee Date: Thu, 16 Mar 2023 15:46:29 +0900 Subject: [PATCH 06/16] =?UTF-8?q?feat:=20=EB=8F=99=EC=9D=BC=ED=95=9C=20?= =?UTF-8?q?=EC=83=81=ED=92=88=20=EC=9D=B4=EB=A6=84=20=EC=A0=80=EC=9E=A5?= =?UTF-8?q?=EC=8B=9C=20=EC=98=88=EC=99=B8=20=EB=B0=9C=EC=83=9D=20=ED=95=98?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/advice/BusinessException.java | 9 +++++++++ .../jscode/spring/product/domain/Product.java | 3 +++ .../spring/product/dto/NewProductRequest.java | 4 ++-- .../exception/DuplicateNameException.java | 13 +++++++++++++ .../exception/ProductNotFoundException.java | 13 +++++++++++++ .../product/repository/ProductRepository.java | 8 ++++++++ .../spring/product/service/ProductService.java | 18 ++++++++++++++++-- .../spring/product/domain/ProductTest.java | 16 ++++++++++++++-- .../product/service/ProductServiceTest.java | 16 +++++++++++++++- 9 files changed, 93 insertions(+), 7 deletions(-) create mode 100644 spring/src/main/java/com/advice/BusinessException.java create mode 100644 spring/src/main/java/com/jscode/spring/product/exception/DuplicateNameException.java create mode 100644 spring/src/main/java/com/jscode/spring/product/exception/ProductNotFoundException.java diff --git a/spring/src/main/java/com/advice/BusinessException.java b/spring/src/main/java/com/advice/BusinessException.java new file mode 100644 index 0000000..94eb91f --- /dev/null +++ b/spring/src/main/java/com/advice/BusinessException.java @@ -0,0 +1,9 @@ +package com.advice; + +public class BusinessException extends RuntimeException { + + public BusinessException(final String message) { + super(message); + } + +} diff --git a/spring/src/main/java/com/jscode/spring/product/domain/Product.java b/spring/src/main/java/com/jscode/spring/product/domain/Product.java index 19b5f90..f46d9f5 100644 --- a/spring/src/main/java/com/jscode/spring/product/domain/Product.java +++ b/spring/src/main/java/com/jscode/spring/product/domain/Product.java @@ -24,4 +24,7 @@ public boolean contains(final Long id) { return Objects.equals(this.id, id); } + public boolean isSameName(final String name) { + return this.name.equals(name); + } } diff --git a/spring/src/main/java/com/jscode/spring/product/dto/NewProductRequest.java b/spring/src/main/java/com/jscode/spring/product/dto/NewProductRequest.java index 3bc1ec3..182d299 100644 --- a/spring/src/main/java/com/jscode/spring/product/dto/NewProductRequest.java +++ b/spring/src/main/java/com/jscode/spring/product/dto/NewProductRequest.java @@ -9,7 +9,7 @@ public class NewProductRequest { private String name; private int price; - public NewProductRequest() { + private NewProductRequest() { } public NewProductRequest(final String name, final int price) { @@ -17,7 +17,7 @@ public NewProductRequest(final String name, final int price) { this.price = price; } - public Product toEntity() { + public Product toDomain() { return new Product(null, name, price); } diff --git a/spring/src/main/java/com/jscode/spring/product/exception/DuplicateNameException.java b/spring/src/main/java/com/jscode/spring/product/exception/DuplicateNameException.java new file mode 100644 index 0000000..5b74567 --- /dev/null +++ b/spring/src/main/java/com/jscode/spring/product/exception/DuplicateNameException.java @@ -0,0 +1,13 @@ +package com.jscode.spring.product.exception; + +import com.advice.BusinessException; + +public class DuplicateNameException extends BusinessException { + + private static final String MESSAGE = "동일한 이름의 상품은 저장할 수 없습니다."; + + public DuplicateNameException() { + super(MESSAGE); + } + +} diff --git a/spring/src/main/java/com/jscode/spring/product/exception/ProductNotFoundException.java b/spring/src/main/java/com/jscode/spring/product/exception/ProductNotFoundException.java new file mode 100644 index 0000000..1a4ba7e --- /dev/null +++ b/spring/src/main/java/com/jscode/spring/product/exception/ProductNotFoundException.java @@ -0,0 +1,13 @@ +package com.jscode.spring.product.exception; + +import com.advice.BusinessException; + +public class ProductNotFoundException extends BusinessException { + + private static final String MESSAGE = "존재하지 않는 상품입니다."; + + public ProductNotFoundException() { + super(MESSAGE); + } + +} diff --git a/spring/src/main/java/com/jscode/spring/product/repository/ProductRepository.java b/spring/src/main/java/com/jscode/spring/product/repository/ProductRepository.java index a53e305..31ee189 100644 --- a/spring/src/main/java/com/jscode/spring/product/repository/ProductRepository.java +++ b/spring/src/main/java/com/jscode/spring/product/repository/ProductRepository.java @@ -35,4 +35,12 @@ public Long save(final Product product) { return product.getId(); } + public Product findByName(final String name) { + return store.stream() + .filter(product -> product.isSameName(name)) + .findAny() + .orElse(null); + } + + } diff --git a/spring/src/main/java/com/jscode/spring/product/service/ProductService.java b/spring/src/main/java/com/jscode/spring/product/service/ProductService.java index 122a016..0e248ed 100644 --- a/spring/src/main/java/com/jscode/spring/product/service/ProductService.java +++ b/spring/src/main/java/com/jscode/spring/product/service/ProductService.java @@ -5,6 +5,8 @@ import com.jscode.spring.product.dto.NewProductRequest; import com.jscode.spring.product.dto.ProductListResponse; import com.jscode.spring.product.dto.ProductResponse; +import com.jscode.spring.product.exception.DuplicateNameException; +import com.jscode.spring.product.exception.ProductNotFoundException; import com.jscode.spring.product.repository.ProductRepository; import java.util.List; import org.springframework.stereotype.Service; @@ -22,18 +24,30 @@ public ProductService(final ProductRepository productRepository, final ExchangeR } public Long saveProduct(final NewProductRequest newProductRequest) { - Product product = newProductRequest.toEntity(); + Product product = newProductRequest.toDomain(); + validateProductName(product); return productRepository.save(product); } + private void validateProductName(final Product product) { + Product findProduct = productRepository.findByName(product.getName()); + if (findProduct != null) { + throw new DuplicateNameException(); + } + } + + // TODO: IllegalArgument? IllegalState? public ProductResponse findProductUsdPrice(final Long productId) { Product product = productRepository.findById(productId); + if (product == null) { + throw new ProductNotFoundException(); + } double usdPrice = exchangeRatesService.convertKrwToUsd(product.getPrice()); return ProductResponse.createUsdPriceResponseOf(product, usdPrice); } /** - * TODO: 테스트하기 어려움 + * TODO: 테스트하기 어려운 코드 */ public ProductListResponse findAllProduct() { List products = productRepository.findAll(); diff --git a/spring/src/test/java/com/jscode/spring/product/domain/ProductTest.java b/spring/src/test/java/com/jscode/spring/product/domain/ProductTest.java index 64d36eb..cc8fee2 100644 --- a/spring/src/test/java/com/jscode/spring/product/domain/ProductTest.java +++ b/spring/src/test/java/com/jscode/spring/product/domain/ProductTest.java @@ -2,17 +2,29 @@ import static org.assertj.core.api.Assertions.assertThat; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; class ProductTest { + Product product; + + @BeforeEach + void setUp() { + product = new Product(1L, "test", 3000); + } + @Test @DisplayName("동일 상품 id 확인") void contains() { - Product product = new Product(1L, "test", 3000); - assertThat(product.contains(1L)).isTrue(); } + @Test + @DisplayName("동일 상품 이름 조회") + void isSameName() { + assertThat(product.isSameName("test")).isTrue(); + } + } \ No newline at end of file diff --git a/spring/src/test/java/com/jscode/spring/product/service/ProductServiceTest.java b/spring/src/test/java/com/jscode/spring/product/service/ProductServiceTest.java index fc83a0b..c393869 100644 --- a/spring/src/test/java/com/jscode/spring/product/service/ProductServiceTest.java +++ b/spring/src/test/java/com/jscode/spring/product/service/ProductServiceTest.java @@ -1,9 +1,11 @@ package com.jscode.spring.product.service; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import com.jscode.spring.product.domain.Product; import com.jscode.spring.product.dto.NewProductRequest; +import com.jscode.spring.product.exception.DuplicateNameException; import com.jscode.spring.product.repository.ProductRepository; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.DisplayName; @@ -22,7 +24,7 @@ class ProductServiceTest { @Test @DisplayName("상품 저장 성공 테스트") - void saveProduct() { + void saveProduct_success() { productService.saveProduct(new NewProductRequest("test", 3000)); Product product = productRepository.findById(4L); @@ -32,4 +34,16 @@ void saveProduct() { ); } + @Test + @DisplayName("동일 이름 상품 저장시 예외 발생 테스트") + void saveDuplicateNameProduct_fail_withException() { + NewProductRequest request1 = new NewProductRequest("sameName", 3000); + NewProductRequest request2 = new NewProductRequest("sameName", 5000); + + productService.saveProduct(request1); + + assertThatThrownBy(() -> productService.saveProduct(request2)) + .isInstanceOf(DuplicateNameException.class) + .hasMessageContaining("동일한 이름의 상품은 저장할 수 없습니다."); + } } \ No newline at end of file From b698ad2d54a9c9989383cf38dcc370fbf5cf4407 Mon Sep 17 00:00:00 2001 From: HiiWee Date: Thu, 16 Mar 2023 19:42:25 +0900 Subject: [PATCH 07/16] =?UTF-8?q?feat:=20=EC=83=81=ED=92=88=20=EC=A0=84?= =?UTF-8?q?=EC=B2=B4=20=EC=A1=B0=ED=9A=8C=20=EB=B0=8F=20=EC=83=81=EC=84=B8?= =?UTF-8?q?=20=EC=98=B5=EC=85=98=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 전체 상품 조회시 이름으로 세부 조회 가능 - 전체 상품 조회시 다른 나라의 금액으로 변환 가능 --- .../service/ExchangeRatesService.java | 8 ++-- .../product/controller/ProductController.java | 19 +++++--- .../spring/product/domain/MonetaryUnit.java | 5 ++ .../product/dto/ProductListResponse.java | 10 +--- .../spring/product/dto/ProductResponse.java | 5 +- .../product/repository/ProductRepository.java | 7 +++ .../product/service/ProductService.java | 47 +++++++++++++++---- .../service/DollarRateServiceTest.java | 17 ++++++- .../repository/ProductRepositoryTest.java | 9 ++-- .../product/service/ProductServiceTest.java | 42 ++++++++++++++++- 10 files changed, 127 insertions(+), 42 deletions(-) create mode 100644 spring/src/main/java/com/jscode/spring/product/domain/MonetaryUnit.java diff --git a/spring/src/main/java/com/jscode/spring/exchange/service/ExchangeRatesService.java b/spring/src/main/java/com/jscode/spring/exchange/service/ExchangeRatesService.java index 9a7623b..018d0f2 100644 --- a/spring/src/main/java/com/jscode/spring/exchange/service/ExchangeRatesService.java +++ b/spring/src/main/java/com/jscode/spring/exchange/service/ExchangeRatesService.java @@ -2,6 +2,7 @@ import com.jscode.spring.exchange.config.ExchangeYamlRead; import com.jscode.spring.exchange.domain.ExchangeRates; +import com.jscode.spring.product.domain.MonetaryUnit; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; @@ -12,21 +13,22 @@ @Service public class ExchangeRatesService { + public static final String EXCHANGE_URL = "https://api.apilayer.com/exchangerates_data/latest?symbols=KRW&base=%s"; private final ExchangeYamlRead exchangeYamlRead; public ExchangeRatesService(final ExchangeYamlRead exchangeYamlRead) { this.exchangeYamlRead = exchangeYamlRead; } - public double convertKrwToUsd(final int krw) { + public double convertKrwTo(final MonetaryUnit monetaryUnit, final int value) { RestTemplate template = new RestTemplate(); ResponseEntity exchange = template.exchange( - "https://api.apilayer.com/exchangerates_data/latest?symbols=KRW&base=USD", + String.format(EXCHANGE_URL, monetaryUnit.toString()), HttpMethod.GET, new HttpEntity<>(generateHeader("apikey")), ExchangeRates.class ); - return krw / exchange.getBody() + return value / exchange.getBody() .findRates("KRW"); } diff --git a/spring/src/main/java/com/jscode/spring/product/controller/ProductController.java b/spring/src/main/java/com/jscode/spring/product/controller/ProductController.java index e665eaa..772b450 100644 --- a/spring/src/main/java/com/jscode/spring/product/controller/ProductController.java +++ b/spring/src/main/java/com/jscode/spring/product/controller/ProductController.java @@ -5,11 +5,13 @@ import com.jscode.spring.product.dto.ProductResponse; import com.jscode.spring.product.service.ProductService; import lombok.extern.slf4j.Slf4j; +import org.springframework.lang.Nullable; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @Slf4j @@ -24,7 +26,8 @@ public ProductController(final ProductService productService) { } /** - * (연습문제) 상품 등록 api + * (연습문제) 상품 등록 api
+ * (미션) 상품 등록 api (동일상품 등록시 실패) */ @PostMapping("/products") public String saveProduct(@RequestBody final NewProductRequest newProductRequest) { @@ -36,15 +39,19 @@ public String saveProduct(@RequestBody final NewProductRequest newProductRequest * (연습문제) 상품 조회 api */ @GetMapping("/products/{productId}") - public ProductResponse findProduct(@PathVariable final Long productId) { - return productService.findProductUsdPrice(productId); + public ProductResponse findProductById(@PathVariable final Long productId, + @RequestParam @Nullable final String monetaryUnit) { + return productService.findProductById(productId, monetaryUnit); } /** - * (연습문제) 전체 상품 조회 + * (연습문제) 전체 상품 조회 (미션) 상품 이름으로 상세조회하는 api
+ * (미션) 전체 상품 조회 필터링 name, monetaryUnit */ @GetMapping("/products") - public ProductListResponse findProducts() { - return productService.findAllProduct(); + public ProductListResponse findProducts(@RequestParam @Nullable final String name, + @RequestParam @Nullable final String monetaryUnit) { + return productService.findAll(name, monetaryUnit); } + } diff --git a/spring/src/main/java/com/jscode/spring/product/domain/MonetaryUnit.java b/spring/src/main/java/com/jscode/spring/product/domain/MonetaryUnit.java new file mode 100644 index 0000000..6a6a4b2 --- /dev/null +++ b/spring/src/main/java/com/jscode/spring/product/domain/MonetaryUnit.java @@ -0,0 +1,5 @@ +package com.jscode.spring.product.domain; + +public enum MonetaryUnit { + USD, KRW +} diff --git a/spring/src/main/java/com/jscode/spring/product/dto/ProductListResponse.java b/spring/src/main/java/com/jscode/spring/product/dto/ProductListResponse.java index 25dc4b2..012f93e 100644 --- a/spring/src/main/java/com/jscode/spring/product/dto/ProductListResponse.java +++ b/spring/src/main/java/com/jscode/spring/product/dto/ProductListResponse.java @@ -1,8 +1,6 @@ package com.jscode.spring.product.dto; -import com.jscode.spring.product.domain.Product; import java.util.List; -import java.util.stream.Collectors; import lombok.Getter; @Getter @@ -10,14 +8,8 @@ public class ProductListResponse { private List productResponses; - private ProductListResponse(final List productResponses) { + public ProductListResponse(final List productResponses) { this.productResponses = productResponses; } - public static ProductListResponse createListFrom(final List products) { - return new ProductListResponse(products.stream() - .map(ProductResponse::createResponseFrom) - .collect(Collectors.toList()) - ); - } } diff --git a/spring/src/main/java/com/jscode/spring/product/dto/ProductResponse.java b/spring/src/main/java/com/jscode/spring/product/dto/ProductResponse.java index 82fd5ec..7ced1fd 100644 --- a/spring/src/main/java/com/jscode/spring/product/dto/ProductResponse.java +++ b/spring/src/main/java/com/jscode/spring/product/dto/ProductResponse.java @@ -16,11 +16,8 @@ private ProductResponse(final Long id, final String name, final double price) { this.price = price; } - public static ProductResponse createUsdPriceResponseOf(final Product product, final double usdPrice) { + public static ProductResponse of(final Product product, final double usdPrice) { return new ProductResponse(product.getId(), product.getName(), usdPrice); } - public static ProductResponse createResponseFrom(final Product product) { - return new ProductResponse(product.getId(), product.getName(), product.getPrice()); - } } diff --git a/spring/src/main/java/com/jscode/spring/product/repository/ProductRepository.java b/spring/src/main/java/com/jscode/spring/product/repository/ProductRepository.java index 31ee189..b480007 100644 --- a/spring/src/main/java/com/jscode/spring/product/repository/ProductRepository.java +++ b/spring/src/main/java/com/jscode/spring/product/repository/ProductRepository.java @@ -4,6 +4,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.stream.Collectors; import org.springframework.stereotype.Repository; @Repository @@ -35,6 +36,7 @@ public Long save(final Product product) { return product.getId(); } + // Optional로 변경 public Product findByName(final String name) { return store.stream() .filter(product -> product.isSameName(name)) @@ -42,5 +44,10 @@ public Product findByName(final String name) { .orElse(null); } + public List findAllByName(final String name) { + return store.stream() + .filter(product -> product.isSameName(name)) + .collect(Collectors.toList()); + } } diff --git a/spring/src/main/java/com/jscode/spring/product/service/ProductService.java b/spring/src/main/java/com/jscode/spring/product/service/ProductService.java index 0e248ed..e91cfeb 100644 --- a/spring/src/main/java/com/jscode/spring/product/service/ProductService.java +++ b/spring/src/main/java/com/jscode/spring/product/service/ProductService.java @@ -1,6 +1,7 @@ package com.jscode.spring.product.service; import com.jscode.spring.exchange.service.ExchangeRatesService; +import com.jscode.spring.product.domain.MonetaryUnit; import com.jscode.spring.product.domain.Product; import com.jscode.spring.product.dto.NewProductRequest; import com.jscode.spring.product.dto.ProductListResponse; @@ -8,7 +9,9 @@ import com.jscode.spring.product.exception.DuplicateNameException; import com.jscode.spring.product.exception.ProductNotFoundException; import com.jscode.spring.product.repository.ProductRepository; +import java.util.ArrayList; import java.util.List; +import org.springframework.lang.Nullable; import org.springframework.stereotype.Service; @Service @@ -36,21 +39,45 @@ private void validateProductName(final Product product) { } } - // TODO: IllegalArgument? IllegalState? - public ProductResponse findProductUsdPrice(final Long productId) { + /** + * TODO: 테스트하기 어려운 코드 + */ + public ProductListResponse findAll(@Nullable final String name, @Nullable final String monetaryUnit) { + if (name == null) { + List products = productRepository.findAll(); + return new ProductListResponse(createConvertedPriceProducts(monetaryUnit, products)); + } + List products = productRepository.findAllByName(name); + if (products.isEmpty()) { + throw new ProductNotFoundException(); + } + return new ProductListResponse(createConvertedPriceProducts(monetaryUnit, products)); + } + + private List createConvertedPriceProducts(final String monetaryUnit, final List products) { + List productResponses = new ArrayList<>(); + for (Product product : products) { + double convertedPrice = convertPriceKrwTo(monetaryUnit, product); + ProductResponse productResponse = ProductResponse.of(product, convertedPrice); + productResponses.add(productResponse); + } + return productResponses; + } + + public ProductResponse findProductById(final Long productId, final String monetaryUnit) { Product product = productRepository.findById(productId); if (product == null) { throw new ProductNotFoundException(); } - double usdPrice = exchangeRatesService.convertKrwToUsd(product.getPrice()); - return ProductResponse.createUsdPriceResponseOf(product, usdPrice); + double convertedPrice = convertPriceKrwTo(monetaryUnit, product); + return ProductResponse.of(product, convertedPrice); } - /** - * TODO: 테스트하기 어려운 코드 - */ - public ProductListResponse findAllProduct() { - List products = productRepository.findAll(); - return ProductListResponse.createListFrom(products); + private double convertPriceKrwTo(final String monetaryUnit, final Product product) { + if (monetaryUnit == null) { + return product.getPrice(); + } + return exchangeRatesService.convertKrwTo(MonetaryUnit.valueOf(monetaryUnit), product.getPrice()); } + } diff --git a/spring/src/test/java/com/jscode/spring/exchange/service/DollarRateServiceTest.java b/spring/src/test/java/com/jscode/spring/exchange/service/DollarRateServiceTest.java index 0dc53ec..6f7bd09 100644 --- a/spring/src/test/java/com/jscode/spring/exchange/service/DollarRateServiceTest.java +++ b/spring/src/test/java/com/jscode/spring/exchange/service/DollarRateServiceTest.java @@ -1,5 +1,9 @@ package com.jscode.spring.exchange.service; +import static org.assertj.core.api.Assertions.assertThat; + +import com.jscode.spring.product.domain.MonetaryUnit; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; @@ -11,7 +15,16 @@ class DollarRateServiceTest { ExchangeRatesService exchangeRatesService; @Test - void convertKrwToUsd() { - System.out.println(exchangeRatesService.convertKrwToUsd(10000)); + @DisplayName("KRW to KRW 변환 성공 테스트") + void convertKrwToKrw_success() { + double convertedPrice = exchangeRatesService.convertKrwTo(MonetaryUnit.KRW, 10000); + assertThat(convertedPrice).isEqualTo(10000.0); + } + + @Test + @DisplayName("KRW to USD 변환 성공 테스트") + void convertKrwToUsd_success() { + double convertedPrice = exchangeRatesService.convertKrwTo(MonetaryUnit.KRW, 10000); + assertThat(convertedPrice).isEqualTo(10000.0); } } \ No newline at end of file diff --git a/spring/src/test/java/com/jscode/spring/product/repository/ProductRepositoryTest.java b/spring/src/test/java/com/jscode/spring/product/repository/ProductRepositoryTest.java index b832d0c..20e0004 100644 --- a/spring/src/test/java/com/jscode/spring/product/repository/ProductRepositoryTest.java +++ b/spring/src/test/java/com/jscode/spring/product/repository/ProductRepositoryTest.java @@ -4,7 +4,6 @@ import com.jscode.spring.product.domain.Product; import java.util.List; -import org.assertj.core.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -33,10 +32,8 @@ void findById_success() { } @Test - @DisplayName("특정 상품 조회 실패시 예외 발생 테스트") - void findById_fail_withException() { - Assertions.assertThatThrownBy(() -> productRepository.findById(11111L)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessageContaining("존재하지 않는 상품입니다."); + @DisplayName("특정 상품 조회 실패시 null값 반환 테스트") + void findById_fail_withNull() { + assertThat(productRepository.findById(1111111111L)).isNull(); } } \ No newline at end of file diff --git a/spring/src/test/java/com/jscode/spring/product/service/ProductServiceTest.java b/spring/src/test/java/com/jscode/spring/product/service/ProductServiceTest.java index c393869..7a67e36 100644 --- a/spring/src/test/java/com/jscode/spring/product/service/ProductServiceTest.java +++ b/spring/src/test/java/com/jscode/spring/product/service/ProductServiceTest.java @@ -3,8 +3,11 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import com.jscode.spring.exchange.service.ExchangeRatesService; +import com.jscode.spring.product.domain.MonetaryUnit; import com.jscode.spring.product.domain.Product; import com.jscode.spring.product.dto.NewProductRequest; +import com.jscode.spring.product.dto.ProductResponse; import com.jscode.spring.product.exception.DuplicateNameException; import com.jscode.spring.product.repository.ProductRepository; import org.junit.jupiter.api.Assertions; @@ -19,14 +22,17 @@ class ProductServiceTest { @Autowired ProductService productService; + @Autowired + ExchangeRatesService exchangeRatesService; + @Autowired ProductRepository productRepository; @Test @DisplayName("상품 저장 성공 테스트") void saveProduct_success() { - productService.saveProduct(new NewProductRequest("test", 3000)); - Product product = productRepository.findById(4L); + Long id = productService.saveProduct(new NewProductRequest("test", 3000)); + Product product = productRepository.findById(id); Assertions.assertAll( () -> assertThat(product.getName()).isEqualTo("test"), @@ -46,4 +52,36 @@ void saveDuplicateNameProduct_fail_withException() { .isInstanceOf(DuplicateNameException.class) .hasMessageContaining("동일한 이름의 상품은 저장할 수 없습니다."); } + + @Test + @DisplayName("단순 상품 ID 조회") + void findProductById_success() { + Long id = productService.saveProduct(new NewProductRequest("basicTest1", 3000)); + + ProductResponse productById = productService.findProductById(id, null); + + assertThat(productById.getPrice()).isEqualTo(3000.0); + } + + @Test + @DisplayName("상품 ID 및 KRW 단위로 조회 성공 테스트") + void findProductById_success_withKRW_monetaryUnit() { + Long id = productService.saveProduct(new NewProductRequest("test1", 3000)); + + ProductResponse productByName = productService.findProductById(id, "KRW"); + + assertThat(productByName.getPrice()).isEqualTo(3000.0); + } + + @Test + @DisplayName("상품 ID 및 USD 단위로 조회 성공 테스트") + void findProductById_success_withUSD_monetaryUnit() { + Long id = productService.saveProduct(new NewProductRequest("test2", 10000)); + double usdPrice = exchangeRatesService.convertKrwTo(MonetaryUnit.USD, 10000); + + ProductResponse productByName = productService.findProductById(id, "USD"); + + assertThat(productByName.getPrice()).isEqualTo(usdPrice); + } + } \ No newline at end of file From 1e41fa15db19a6d87400f50106b2e27c5da54e69 Mon Sep 17 00:00:00 2001 From: HiiWee Date: Thu, 16 Mar 2023 19:58:26 +0900 Subject: [PATCH 08/16] =?UTF-8?q?feat:=20=EB=B9=84=EC=A6=88=EB=8B=88?= =?UTF-8?q?=EC=8A=A4=20=EB=A1=9C=EC=A7=81=20=EC=98=88=EC=99=B8=20=EB=B0=9C?= =?UTF-8?q?=EC=83=9D=EC=8B=9C=20=EA=B3=B5=ED=86=B5=20=EC=B2=98=EB=A6=AC=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../spring/advice/BadRequestException.java | 9 ++++++++ .../spring}/advice/BusinessException.java | 2 +- .../spring/advice/ControllerAdvice.java | 21 +++++++++++++++++++ .../jscode/spring/advice/ErrorResponse.java | 14 +++++++++++++ .../spring/advice/NotFoundException.java | 9 ++++++++ .../exception/DuplicateNameException.java | 4 ++-- .../exception/ProductNotFoundException.java | 4 ++-- 7 files changed, 58 insertions(+), 5 deletions(-) create mode 100644 spring/src/main/java/com/jscode/spring/advice/BadRequestException.java rename spring/src/main/java/com/{ => jscode/spring}/advice/BusinessException.java (81%) create mode 100644 spring/src/main/java/com/jscode/spring/advice/ControllerAdvice.java create mode 100644 spring/src/main/java/com/jscode/spring/advice/ErrorResponse.java create mode 100644 spring/src/main/java/com/jscode/spring/advice/NotFoundException.java diff --git a/spring/src/main/java/com/jscode/spring/advice/BadRequestException.java b/spring/src/main/java/com/jscode/spring/advice/BadRequestException.java new file mode 100644 index 0000000..18d5447 --- /dev/null +++ b/spring/src/main/java/com/jscode/spring/advice/BadRequestException.java @@ -0,0 +1,9 @@ +package com.jscode.spring.advice; + +public class BadRequestException extends BusinessException { + + public BadRequestException(final String message) { + super(message); + } + +} diff --git a/spring/src/main/java/com/advice/BusinessException.java b/spring/src/main/java/com/jscode/spring/advice/BusinessException.java similarity index 81% rename from spring/src/main/java/com/advice/BusinessException.java rename to spring/src/main/java/com/jscode/spring/advice/BusinessException.java index 94eb91f..4d5a6a3 100644 --- a/spring/src/main/java/com/advice/BusinessException.java +++ b/spring/src/main/java/com/jscode/spring/advice/BusinessException.java @@ -1,4 +1,4 @@ -package com.advice; +package com.jscode.spring.advice; public class BusinessException extends RuntimeException { diff --git a/spring/src/main/java/com/jscode/spring/advice/ControllerAdvice.java b/spring/src/main/java/com/jscode/spring/advice/ControllerAdvice.java new file mode 100644 index 0000000..925a2c4 --- /dev/null +++ b/spring/src/main/java/com/jscode/spring/advice/ControllerAdvice.java @@ -0,0 +1,21 @@ +package com.jscode.spring.advice; + +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +@RestControllerAdvice +public class ControllerAdvice { + + @ExceptionHandler(NotFoundException.class) + ResponseEntity handleNotFoundException(NotFoundException e) { + return ResponseEntity.status(HttpStatus.NOT_FOUND).body(new ErrorResponse(e.getMessage())); + } + + @ExceptionHandler(BadRequestException.class) + ResponseEntity handleBadRequestException(BadRequestException e) { + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(new ErrorResponse(e.getMessage())); + } + +} diff --git a/spring/src/main/java/com/jscode/spring/advice/ErrorResponse.java b/spring/src/main/java/com/jscode/spring/advice/ErrorResponse.java new file mode 100644 index 0000000..e4e8226 --- /dev/null +++ b/spring/src/main/java/com/jscode/spring/advice/ErrorResponse.java @@ -0,0 +1,14 @@ +package com.jscode.spring.advice; + +import lombok.Getter; + +@Getter +public class ErrorResponse { + + private String message; + + public ErrorResponse(final String message) { + this.message = message; + } + +} diff --git a/spring/src/main/java/com/jscode/spring/advice/NotFoundException.java b/spring/src/main/java/com/jscode/spring/advice/NotFoundException.java new file mode 100644 index 0000000..b441a33 --- /dev/null +++ b/spring/src/main/java/com/jscode/spring/advice/NotFoundException.java @@ -0,0 +1,9 @@ +package com.jscode.spring.advice; + +public class NotFoundException extends BusinessException { + + public NotFoundException(final String message) { + super(message); + } + +} diff --git a/spring/src/main/java/com/jscode/spring/product/exception/DuplicateNameException.java b/spring/src/main/java/com/jscode/spring/product/exception/DuplicateNameException.java index 5b74567..6e9f7e9 100644 --- a/spring/src/main/java/com/jscode/spring/product/exception/DuplicateNameException.java +++ b/spring/src/main/java/com/jscode/spring/product/exception/DuplicateNameException.java @@ -1,8 +1,8 @@ package com.jscode.spring.product.exception; -import com.advice.BusinessException; +import com.jscode.spring.advice.BadRequestException; -public class DuplicateNameException extends BusinessException { +public class DuplicateNameException extends BadRequestException { private static final String MESSAGE = "동일한 이름의 상품은 저장할 수 없습니다."; diff --git a/spring/src/main/java/com/jscode/spring/product/exception/ProductNotFoundException.java b/spring/src/main/java/com/jscode/spring/product/exception/ProductNotFoundException.java index 1a4ba7e..b90c2b2 100644 --- a/spring/src/main/java/com/jscode/spring/product/exception/ProductNotFoundException.java +++ b/spring/src/main/java/com/jscode/spring/product/exception/ProductNotFoundException.java @@ -1,8 +1,8 @@ package com.jscode.spring.product.exception; -import com.advice.BusinessException; +import com.jscode.spring.advice.NotFoundException; -public class ProductNotFoundException extends BusinessException { +public class ProductNotFoundException extends NotFoundException { private static final String MESSAGE = "존재하지 않는 상품입니다."; From 4cafbecf157669ce96dbfa936abeb3ff814f3aa9 Mon Sep 17 00:00:00 2001 From: HiiWee Date: Thu, 16 Mar 2023 20:19:22 +0900 Subject: [PATCH 09/16] =?UTF-8?q?refactor:=20null=20=EB=B0=98=ED=99=98=20?= =?UTF-8?q?=EB=8B=A8=EA=B1=B4=20=EC=A1=B0=ED=9A=8C=EB=A5=BC=20optional?= =?UTF-8?q?=EC=9D=84=20=EB=B0=98=ED=99=98=ED=95=98=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jscode/spring/advice/ControllerAdvice.java | 3 +++ .../product/controller/ProductController.java | 2 +- .../product/repository/ProductRepository.java | 11 +++++------ .../spring/product/service/ProductService.java | 18 ++++++------------ .../repository/ProductRepositoryTest.java | 4 ++-- .../product/service/ProductServiceTest.java | 2 +- 6 files changed, 18 insertions(+), 22 deletions(-) diff --git a/spring/src/main/java/com/jscode/spring/advice/ControllerAdvice.java b/spring/src/main/java/com/jscode/spring/advice/ControllerAdvice.java index 925a2c4..3a8992b 100644 --- a/spring/src/main/java/com/jscode/spring/advice/ControllerAdvice.java +++ b/spring/src/main/java/com/jscode/spring/advice/ControllerAdvice.java @@ -5,6 +5,9 @@ import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; +/** + * (미션) 조회/등록 실패할 때 응답 interface 구현 + */ @RestControllerAdvice public class ControllerAdvice { diff --git a/spring/src/main/java/com/jscode/spring/product/controller/ProductController.java b/spring/src/main/java/com/jscode/spring/product/controller/ProductController.java index 772b450..d742f71 100644 --- a/spring/src/main/java/com/jscode/spring/product/controller/ProductController.java +++ b/spring/src/main/java/com/jscode/spring/product/controller/ProductController.java @@ -46,7 +46,7 @@ public ProductResponse findProductById(@PathVariable final Long productId, /** * (연습문제) 전체 상품 조회 (미션) 상품 이름으로 상세조회하는 api
- * (미션) 전체 상품 조회 필터링 name, monetaryUnit + * (미션) 상품 상세 조회 api */ @GetMapping("/products") public ProductListResponse findProducts(@RequestParam @Nullable final String name, diff --git a/spring/src/main/java/com/jscode/spring/product/repository/ProductRepository.java b/spring/src/main/java/com/jscode/spring/product/repository/ProductRepository.java index b480007..48d3bf0 100644 --- a/spring/src/main/java/com/jscode/spring/product/repository/ProductRepository.java +++ b/spring/src/main/java/com/jscode/spring/product/repository/ProductRepository.java @@ -4,6 +4,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Optional; import java.util.stream.Collectors; import org.springframework.stereotype.Repository; @@ -23,11 +24,10 @@ public List findAll() { return Collections.unmodifiableList(store); } - public Product findById(final Long id) { + public Optional findById(final Long id) { return store.stream() .filter(product -> product.contains(id)) - .findAny() - .orElse(null); + .findAny(); } public Long save(final Product product) { @@ -37,11 +37,10 @@ public Long save(final Product product) { } // Optional로 변경 - public Product findByName(final String name) { + public Optional findByName(final String name) { return store.stream() .filter(product -> product.isSameName(name)) - .findAny() - .orElse(null); + .findAny(); } public List findAllByName(final String name) { diff --git a/spring/src/main/java/com/jscode/spring/product/service/ProductService.java b/spring/src/main/java/com/jscode/spring/product/service/ProductService.java index e91cfeb..762b513 100644 --- a/spring/src/main/java/com/jscode/spring/product/service/ProductService.java +++ b/spring/src/main/java/com/jscode/spring/product/service/ProductService.java @@ -28,15 +28,10 @@ public ProductService(final ProductRepository productRepository, final ExchangeR public Long saveProduct(final NewProductRequest newProductRequest) { Product product = newProductRequest.toDomain(); - validateProductName(product); - return productRepository.save(product); - } - - private void validateProductName(final Product product) { - Product findProduct = productRepository.findByName(product.getName()); - if (findProduct != null) { + if (productRepository.findByName(product.getName()).isPresent()) { throw new DuplicateNameException(); } + return productRepository.save(product); } /** @@ -54,7 +49,8 @@ public ProductListResponse findAll(@Nullable final String name, @Nullable final return new ProductListResponse(createConvertedPriceProducts(monetaryUnit, products)); } - private List createConvertedPriceProducts(final String monetaryUnit, final List products) { + private List createConvertedPriceProducts(final String monetaryUnit, + final List products) { List productResponses = new ArrayList<>(); for (Product product : products) { double convertedPrice = convertPriceKrwTo(monetaryUnit, product); @@ -65,10 +61,8 @@ private List createConvertedPriceProducts(final String monetary } public ProductResponse findProductById(final Long productId, final String monetaryUnit) { - Product product = productRepository.findById(productId); - if (product == null) { - throw new ProductNotFoundException(); - } + Product product = productRepository.findById(productId) + .orElseThrow(ProductNotFoundException::new); double convertedPrice = convertPriceKrwTo(monetaryUnit, product); return ProductResponse.of(product, convertedPrice); } diff --git a/spring/src/test/java/com/jscode/spring/product/repository/ProductRepositoryTest.java b/spring/src/test/java/com/jscode/spring/product/repository/ProductRepositoryTest.java index 20e0004..5eca8a9 100644 --- a/spring/src/test/java/com/jscode/spring/product/repository/ProductRepositoryTest.java +++ b/spring/src/test/java/com/jscode/spring/product/repository/ProductRepositoryTest.java @@ -27,13 +27,13 @@ void findAll() { @Test @DisplayName("특정 상품 조회 성공 테스트") void findById_success() { - Product product = productRepository.findById(1L); + Product product = productRepository.findById(1L).get(); assertThat(product.getId()).isEqualTo(1L); } @Test @DisplayName("특정 상품 조회 실패시 null값 반환 테스트") void findById_fail_withNull() { - assertThat(productRepository.findById(1111111111L)).isNull(); + assertThat(productRepository.findById(1111111111L)).isEmpty(); } } \ No newline at end of file diff --git a/spring/src/test/java/com/jscode/spring/product/service/ProductServiceTest.java b/spring/src/test/java/com/jscode/spring/product/service/ProductServiceTest.java index 7a67e36..81302a5 100644 --- a/spring/src/test/java/com/jscode/spring/product/service/ProductServiceTest.java +++ b/spring/src/test/java/com/jscode/spring/product/service/ProductServiceTest.java @@ -32,7 +32,7 @@ class ProductServiceTest { @DisplayName("상품 저장 성공 테스트") void saveProduct_success() { Long id = productService.saveProduct(new NewProductRequest("test", 3000)); - Product product = productRepository.findById(id); + Product product = productRepository.findById(id).get(); Assertions.assertAll( () -> assertThat(product.getName()).isEqualTo("test"), From f92754a59f177d59260604fea43b3f0797389ded Mon Sep 17 00:00:00 2001 From: HiiWee Date: Thu, 16 Mar 2023 20:30:36 +0900 Subject: [PATCH 10/16] =?UTF-8?q?style:=20=EB=AF=B8=EC=85=98=20=EC=A0=95?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/jscode/spring/advice/ControllerAdvice.java | 2 +- .../spring/product/controller/ProductController.java | 10 +++++----- .../spring/product/repository/ProductRepository.java | 1 - 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/spring/src/main/java/com/jscode/spring/advice/ControllerAdvice.java b/spring/src/main/java/com/jscode/spring/advice/ControllerAdvice.java index 3a8992b..1fc272b 100644 --- a/spring/src/main/java/com/jscode/spring/advice/ControllerAdvice.java +++ b/spring/src/main/java/com/jscode/spring/advice/ControllerAdvice.java @@ -6,7 +6,7 @@ import org.springframework.web.bind.annotation.RestControllerAdvice; /** - * (미션) 조회/등록 실패할 때 응답 interface 구현 + * (미션 3 ) 조회/등록 실패할 때 응답 interface 구현 */ @RestControllerAdvice public class ControllerAdvice { diff --git a/spring/src/main/java/com/jscode/spring/product/controller/ProductController.java b/spring/src/main/java/com/jscode/spring/product/controller/ProductController.java index d742f71..2e52f8f 100644 --- a/spring/src/main/java/com/jscode/spring/product/controller/ProductController.java +++ b/spring/src/main/java/com/jscode/spring/product/controller/ProductController.java @@ -26,8 +26,8 @@ public ProductController(final ProductService productService) { } /** - * (연습문제) 상품 등록 api
- * (미션) 상품 등록 api (동일상품 등록시 실패) + * (연습문제 1 ) 상품 등록 api
+ * (미션 1 ) 상품 등록 api (동일상품 등록시 실패) */ @PostMapping("/products") public String saveProduct(@RequestBody final NewProductRequest newProductRequest) { @@ -36,7 +36,7 @@ public String saveProduct(@RequestBody final NewProductRequest newProductRequest } /** - * (연습문제) 상품 조회 api + * (연습문제 2 ) 상품 조회 api */ @GetMapping("/products/{productId}") public ProductResponse findProductById(@PathVariable final Long productId, @@ -45,8 +45,8 @@ public ProductResponse findProductById(@PathVariable final Long productId, } /** - * (연습문제) 전체 상품 조회 (미션) 상품 이름으로 상세조회하는 api
- * (미션) 상품 상세 조회 api + * (연습문제 3 ) 전체 상품 조회 (미션) 상품 이름으로 상세조회하는 api
+ * (미션 2 ) 상품 상세 조회 api */ @GetMapping("/products") public ProductListResponse findProducts(@RequestParam @Nullable final String name, diff --git a/spring/src/main/java/com/jscode/spring/product/repository/ProductRepository.java b/spring/src/main/java/com/jscode/spring/product/repository/ProductRepository.java index 48d3bf0..59ab40f 100644 --- a/spring/src/main/java/com/jscode/spring/product/repository/ProductRepository.java +++ b/spring/src/main/java/com/jscode/spring/product/repository/ProductRepository.java @@ -36,7 +36,6 @@ public Long save(final Product product) { return product.getId(); } - // Optional로 변경 public Optional findByName(final String name) { return store.stream() .filter(product -> product.isSameName(name)) From d306dfdce5fa3203c9c6ad7829e581a07381bbec Mon Sep 17 00:00:00 2001 From: HiiWee Date: Fri, 17 Mar 2023 01:42:29 +0900 Subject: [PATCH 11/16] =?UTF-8?q?test:=20=ED=8A=B9=EC=A0=95=20=EC=9D=B4?= =?UTF-8?q?=EB=A6=84=EC=97=90=20=EB=8C=80=ED=95=9C=20=EC=A0=84=EC=B2=B4=20?= =?UTF-8?q?=EC=83=81=ED=92=88=20=EC=A1=B0=ED=9A=8C=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../spring/product/repository/ProductRepository.java | 12 ++++++------ .../spring/product/service/ProductServiceTest.java | 12 ++++++++++++ 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/spring/src/main/java/com/jscode/spring/product/repository/ProductRepository.java b/spring/src/main/java/com/jscode/spring/product/repository/ProductRepository.java index 59ab40f..6821757 100644 --- a/spring/src/main/java/com/jscode/spring/product/repository/ProductRepository.java +++ b/spring/src/main/java/com/jscode/spring/product/repository/ProductRepository.java @@ -24,6 +24,12 @@ public List findAll() { return Collections.unmodifiableList(store); } + public List findAllByName(final String name) { + return store.stream() + .filter(product -> product.isSameName(name)) + .collect(Collectors.toUnmodifiableList()); + } + public Optional findById(final Long id) { return store.stream() .filter(product -> product.contains(id)) @@ -42,10 +48,4 @@ public Optional findByName(final String name) { .findAny(); } - public List findAllByName(final String name) { - return store.stream() - .filter(product -> product.isSameName(name)) - .collect(Collectors.toList()); - } - } diff --git a/spring/src/test/java/com/jscode/spring/product/service/ProductServiceTest.java b/spring/src/test/java/com/jscode/spring/product/service/ProductServiceTest.java index 81302a5..5d427d2 100644 --- a/spring/src/test/java/com/jscode/spring/product/service/ProductServiceTest.java +++ b/spring/src/test/java/com/jscode/spring/product/service/ProductServiceTest.java @@ -9,6 +9,7 @@ import com.jscode.spring.product.dto.NewProductRequest; import com.jscode.spring.product.dto.ProductResponse; import com.jscode.spring.product.exception.DuplicateNameException; +import com.jscode.spring.product.exception.ProductNotFoundException; import com.jscode.spring.product.repository.ProductRepository; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.DisplayName; @@ -40,6 +41,17 @@ void saveProduct_success() { ); } + @Test + @DisplayName("없는 name에 대한 전체 상품 조회 실패 테스트") + void findAll_fail_withInvalidName() { + String name = "nothing"; + + assertThatThrownBy(() -> productService.findAll(name, null)) + .isInstanceOf(ProductNotFoundException.class) + .hasMessageContaining("존재하지 않는 상품입니다."); + } + + @Test @DisplayName("동일 이름 상품 저장시 예외 발생 테스트") void saveDuplicateNameProduct_fail_withException() { From 5be2946f9ad2654016583ff6018f8f39c04fbd59 Mon Sep 17 00:00:00 2001 From: HiiWee Date: Fri, 17 Mar 2023 03:18:03 +0900 Subject: [PATCH 12/16] =?UTF-8?q?refactor:=20=EC=83=81=ED=92=88=20?= =?UTF-8?q?=EC=A0=80=EC=9E=A5=EC=8B=9C=20=EB=AA=85=EC=8B=9C=EC=A0=81?= =?UTF-8?q?=EC=9D=B8=20dto=20=EB=B0=98=ED=99=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../product/controller/ProductController.java | 5 +++-- .../spring/product/dto/ProductSaveResponse.java | 14 ++++++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) create mode 100644 spring/src/main/java/com/jscode/spring/product/dto/ProductSaveResponse.java diff --git a/spring/src/main/java/com/jscode/spring/product/controller/ProductController.java b/spring/src/main/java/com/jscode/spring/product/controller/ProductController.java index 2e52f8f..3dd9c43 100644 --- a/spring/src/main/java/com/jscode/spring/product/controller/ProductController.java +++ b/spring/src/main/java/com/jscode/spring/product/controller/ProductController.java @@ -3,6 +3,7 @@ import com.jscode.spring.product.dto.NewProductRequest; import com.jscode.spring.product.dto.ProductListResponse; import com.jscode.spring.product.dto.ProductResponse; +import com.jscode.spring.product.dto.ProductSaveResponse; import com.jscode.spring.product.service.ProductService; import lombok.extern.slf4j.Slf4j; import org.springframework.lang.Nullable; @@ -30,9 +31,9 @@ public ProductController(final ProductService productService) { * (미션 1 ) 상품 등록 api (동일상품 등록시 실패) */ @PostMapping("/products") - public String saveProduct(@RequestBody final NewProductRequest newProductRequest) { + public ProductSaveResponse saveProduct(@RequestBody final NewProductRequest newProductRequest) { Long generatedId = productService.saveProduct(newProductRequest); - return String.valueOf(generatedId); + return new ProductSaveResponse(generatedId); } /** diff --git a/spring/src/main/java/com/jscode/spring/product/dto/ProductSaveResponse.java b/spring/src/main/java/com/jscode/spring/product/dto/ProductSaveResponse.java new file mode 100644 index 0000000..da04a84 --- /dev/null +++ b/spring/src/main/java/com/jscode/spring/product/dto/ProductSaveResponse.java @@ -0,0 +1,14 @@ +package com.jscode.spring.product.dto; + +import lombok.Getter; + +@Getter +public class ProductSaveResponse { + + private Long savedId; + + public ProductSaveResponse(final Long savedId) { + this.savedId = savedId; + } + +} From c6cbcf6978c1b66f445dec535f40611cbd9173c1 Mon Sep 17 00:00:00 2001 From: HiiWee Date: Sat, 18 Mar 2023 15:41:14 +0900 Subject: [PATCH 13/16] =?UTF-8?q?fix:=20id=EA=B0=92=20=EC=83=9D=EC=84=B1?= =?UTF-8?q?=EC=8B=9C=20=EB=8F=99=EC=8B=9C=EC=84=B1=20=EB=AC=B8=EC=A0=9C=20?= =?UTF-8?q?=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jscode/spring/product/repository/ProductRepository.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/spring/src/main/java/com/jscode/spring/product/repository/ProductRepository.java b/spring/src/main/java/com/jscode/spring/product/repository/ProductRepository.java index 6821757..debf6c5 100644 --- a/spring/src/main/java/com/jscode/spring/product/repository/ProductRepository.java +++ b/spring/src/main/java/com/jscode/spring/product/repository/ProductRepository.java @@ -5,6 +5,7 @@ import java.util.Collections; import java.util.List; import java.util.Optional; +import java.util.concurrent.atomic.AtomicLong; import java.util.stream.Collectors; import org.springframework.stereotype.Repository; @@ -12,7 +13,7 @@ public class ProductRepository { private static final List store = new ArrayList<>(); - private static Long id = 3L; + private static final AtomicLong counter = new AtomicLong(3L); static { store.add(new Product(1L, "컴퓨터", 3_000_000)); @@ -37,7 +38,7 @@ public Optional findById(final Long id) { } public Long save(final Product product) { - product.generateId(++id); + product.generateId(counter.incrementAndGet()); store.add(product); return product.getId(); } From f48aab14a7bfed529f63bed44d695cf875ea6a69 Mon Sep 17 00:00:00 2001 From: HiiWee Date: Mon, 20 Mar 2023 16:17:47 +0900 Subject: [PATCH 14/16] =?UTF-8?q?refactor:=20=EB=B9=84=EC=8A=B7=ED=95=9C?= =?UTF-8?q?=20=EA=B8=B0=EB=8A=A5=EC=9D=98=20=EB=A9=94=EC=86=8C=EB=93=9C?= =?UTF-8?q?=EB=AA=85=EC=9D=84=20=ED=99=95=EC=8B=A4=ED=95=99=20=EA=B5=AC?= =?UTF-8?q?=EB=B6=84=ED=95=98=EB=8F=84=EB=A1=9D=20=EC=9D=B4=EB=A6=84=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jscode/spring/exchange/service/ExchangeRatesService.java | 1 + .../src/main/java/com/jscode/spring/product/domain/Product.java | 2 +- .../com/jscode/spring/product/repository/ProductRepository.java | 2 +- .../test/java/com/jscode/spring/product/domain/ProductTest.java | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/spring/src/main/java/com/jscode/spring/exchange/service/ExchangeRatesService.java b/spring/src/main/java/com/jscode/spring/exchange/service/ExchangeRatesService.java index 018d0f2..e3eb205 100644 --- a/spring/src/main/java/com/jscode/spring/exchange/service/ExchangeRatesService.java +++ b/spring/src/main/java/com/jscode/spring/exchange/service/ExchangeRatesService.java @@ -14,6 +14,7 @@ public class ExchangeRatesService { public static final String EXCHANGE_URL = "https://api.apilayer.com/exchangerates_data/latest?symbols=KRW&base=%s"; + private final ExchangeYamlRead exchangeYamlRead; public ExchangeRatesService(final ExchangeYamlRead exchangeYamlRead) { diff --git a/spring/src/main/java/com/jscode/spring/product/domain/Product.java b/spring/src/main/java/com/jscode/spring/product/domain/Product.java index f46d9f5..d233598 100644 --- a/spring/src/main/java/com/jscode/spring/product/domain/Product.java +++ b/spring/src/main/java/com/jscode/spring/product/domain/Product.java @@ -20,7 +20,7 @@ public void generateId(final Long id) { this.id = id; } - public boolean contains(final Long id) { + public boolean isSameId(final Long id) { return Objects.equals(this.id, id); } diff --git a/spring/src/main/java/com/jscode/spring/product/repository/ProductRepository.java b/spring/src/main/java/com/jscode/spring/product/repository/ProductRepository.java index debf6c5..79d8585 100644 --- a/spring/src/main/java/com/jscode/spring/product/repository/ProductRepository.java +++ b/spring/src/main/java/com/jscode/spring/product/repository/ProductRepository.java @@ -33,7 +33,7 @@ public List findAllByName(final String name) { public Optional findById(final Long id) { return store.stream() - .filter(product -> product.contains(id)) + .filter(product -> product.isSameId(id)) .findAny(); } diff --git a/spring/src/test/java/com/jscode/spring/product/domain/ProductTest.java b/spring/src/test/java/com/jscode/spring/product/domain/ProductTest.java index cc8fee2..63c3ecd 100644 --- a/spring/src/test/java/com/jscode/spring/product/domain/ProductTest.java +++ b/spring/src/test/java/com/jscode/spring/product/domain/ProductTest.java @@ -18,7 +18,7 @@ void setUp() { @Test @DisplayName("동일 상품 id 확인") void contains() { - assertThat(product.contains(1L)).isTrue(); + assertThat(product.isSameId(1L)).isTrue(); } @Test From 446f649373dba0442650fe733dbeebea79c10c95 Mon Sep 17 00:00:00 2001 From: HiiWee Date: Mon, 20 Mar 2023 16:30:44 +0900 Subject: [PATCH 15/16] =?UTF-8?q?test:=20=EC=A0=84=EC=B2=B4=20=EC=83=81?= =?UTF-8?q?=ED=92=88=20=EC=A1=B0=ED=9A=8C=EC=97=90=20=EB=8C=80=ED=95=9C=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../product/dto/ProductListResponse.java | 4 ++++ .../spring/product/dto/ProductResponse.java | 22 +++++++++++++++++-- .../product/service/ProductServiceTest.java | 16 ++++++++++++++ 3 files changed, 40 insertions(+), 2 deletions(-) diff --git a/spring/src/main/java/com/jscode/spring/product/dto/ProductListResponse.java b/spring/src/main/java/com/jscode/spring/product/dto/ProductListResponse.java index 012f93e..eacaadb 100644 --- a/spring/src/main/java/com/jscode/spring/product/dto/ProductListResponse.java +++ b/spring/src/main/java/com/jscode/spring/product/dto/ProductListResponse.java @@ -12,4 +12,8 @@ public ProductListResponse(final List productResponses) { this.productResponses = productResponses; } + public boolean contains(final ProductResponse productResponse) { + return productResponses.contains(productResponse); + } + } diff --git a/spring/src/main/java/com/jscode/spring/product/dto/ProductResponse.java b/spring/src/main/java/com/jscode/spring/product/dto/ProductResponse.java index 7ced1fd..6f2dfde 100644 --- a/spring/src/main/java/com/jscode/spring/product/dto/ProductResponse.java +++ b/spring/src/main/java/com/jscode/spring/product/dto/ProductResponse.java @@ -1,6 +1,7 @@ package com.jscode.spring.product.dto; import com.jscode.spring.product.domain.Product; +import java.util.Objects; import lombok.Getter; @Getter @@ -16,8 +17,25 @@ private ProductResponse(final Long id, final String name, final double price) { this.price = price; } - public static ProductResponse of(final Product product, final double usdPrice) { - return new ProductResponse(product.getId(), product.getName(), usdPrice); + public static ProductResponse of(final Product product, final double price) { + return new ProductResponse(product.getId(), product.getName(), price); + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + ProductResponse that = (ProductResponse) o; + return Double.compare(that.price, price) == 0 && id.equals(that.id) && name.equals(that.name); + } + + @Override + public int hashCode() { + return Objects.hash(id, name, price); } } diff --git a/spring/src/test/java/com/jscode/spring/product/service/ProductServiceTest.java b/spring/src/test/java/com/jscode/spring/product/service/ProductServiceTest.java index 5d427d2..0e5040b 100644 --- a/spring/src/test/java/com/jscode/spring/product/service/ProductServiceTest.java +++ b/spring/src/test/java/com/jscode/spring/product/service/ProductServiceTest.java @@ -7,6 +7,7 @@ import com.jscode.spring.product.domain.MonetaryUnit; import com.jscode.spring.product.domain.Product; import com.jscode.spring.product.dto.NewProductRequest; +import com.jscode.spring.product.dto.ProductListResponse; import com.jscode.spring.product.dto.ProductResponse; import com.jscode.spring.product.exception.DuplicateNameException; import com.jscode.spring.product.exception.ProductNotFoundException; @@ -51,6 +52,21 @@ void findAll_fail_withInvalidName() { .hasMessageContaining("존재하지 않는 상품입니다."); } + @Test + @DisplayName("전체 상품 조회 성공 테스트") + void findAll_success() { + ProductResponse productResponse1 = ProductResponse.of(new Product(1L, "컴퓨터", 3_000_000), 3000000); + ProductResponse productResponse2 = ProductResponse.of(new Product(2L, "키보드", 100_000), 100000); + ProductResponse productResponse3 = ProductResponse.of(new Product(3L, "마우스", 50_000), 50000); + + ProductListResponse products = productService.findAll(null, null); + + Assertions.assertAll( + () -> assertThat(products.contains(productResponse1)).isTrue(), + () -> assertThat(products.contains(productResponse2)).isTrue(), + () -> assertThat(products.contains(productResponse3)).isTrue() + ); + } @Test @DisplayName("동일 이름 상품 저장시 예외 발생 테스트") From 50cb1cda9cf26596712b62a377a2bf5454a8e234 Mon Sep 17 00:00:00 2001 From: HiiWee Date: Mon, 20 Mar 2023 16:43:21 +0900 Subject: [PATCH 16/16] =?UTF-8?q?refactor:=20=EB=91=90=20=EA=B0=9C=20?= =?UTF-8?q?=EC=9D=B4=EC=83=81=EC=9D=98=20=EC=9D=BC=EC=9D=84=20=ED=95=98?= =?UTF-8?q?=EB=8A=94=20=EC=A0=84=EC=B2=B4=20=EC=83=81=ED=92=88=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20=EB=A9=94=EC=86=8C=EB=93=9C=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../product/controller/ProductController.java | 6 +++++- .../spring/product/service/ProductService.java | 17 ++++++++--------- .../product/service/ProductServiceTest.java | 4 ++-- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/spring/src/main/java/com/jscode/spring/product/controller/ProductController.java b/spring/src/main/java/com/jscode/spring/product/controller/ProductController.java index 3dd9c43..86c6ed0 100644 --- a/spring/src/main/java/com/jscode/spring/product/controller/ProductController.java +++ b/spring/src/main/java/com/jscode/spring/product/controller/ProductController.java @@ -5,6 +5,7 @@ import com.jscode.spring.product.dto.ProductResponse; import com.jscode.spring.product.dto.ProductSaveResponse; import com.jscode.spring.product.service.ProductService; +import java.util.Objects; import lombok.extern.slf4j.Slf4j; import org.springframework.lang.Nullable; import org.springframework.web.bind.annotation.GetMapping; @@ -52,7 +53,10 @@ public ProductResponse findProductById(@PathVariable final Long productId, @GetMapping("/products") public ProductListResponse findProducts(@RequestParam @Nullable final String name, @RequestParam @Nullable final String monetaryUnit) { - return productService.findAll(name, monetaryUnit); + if (Objects.isNull(name)) { + return productService.findAll(monetaryUnit); + } + return productService.findAllByName(name, monetaryUnit); } } diff --git a/spring/src/main/java/com/jscode/spring/product/service/ProductService.java b/spring/src/main/java/com/jscode/spring/product/service/ProductService.java index 762b513..b30e024 100644 --- a/spring/src/main/java/com/jscode/spring/product/service/ProductService.java +++ b/spring/src/main/java/com/jscode/spring/product/service/ProductService.java @@ -11,6 +11,7 @@ import com.jscode.spring.product.repository.ProductRepository; import java.util.ArrayList; import java.util.List; +import java.util.Objects; import org.springframework.lang.Nullable; import org.springframework.stereotype.Service; @@ -34,14 +35,7 @@ public Long saveProduct(final NewProductRequest newProductRequest) { return productRepository.save(product); } - /** - * TODO: 테스트하기 어려운 코드 - */ - public ProductListResponse findAll(@Nullable final String name, @Nullable final String monetaryUnit) { - if (name == null) { - List products = productRepository.findAll(); - return new ProductListResponse(createConvertedPriceProducts(monetaryUnit, products)); - } + public ProductListResponse findAllByName(@Nullable final String name, @Nullable final String monetaryUnit) { List products = productRepository.findAllByName(name); if (products.isEmpty()) { throw new ProductNotFoundException(); @@ -49,6 +43,11 @@ public ProductListResponse findAll(@Nullable final String name, @Nullable final return new ProductListResponse(createConvertedPriceProducts(monetaryUnit, products)); } + public ProductListResponse findAll(final String monetaryUnit) { + List products = productRepository.findAll(); + return new ProductListResponse(createConvertedPriceProducts(monetaryUnit, products)); + } + private List createConvertedPriceProducts(final String monetaryUnit, final List products) { List productResponses = new ArrayList<>(); @@ -68,7 +67,7 @@ public ProductResponse findProductById(final Long productId, final String moneta } private double convertPriceKrwTo(final String monetaryUnit, final Product product) { - if (monetaryUnit == null) { + if (Objects.isNull(monetaryUnit)) { return product.getPrice(); } return exchangeRatesService.convertKrwTo(MonetaryUnit.valueOf(monetaryUnit), product.getPrice()); diff --git a/spring/src/test/java/com/jscode/spring/product/service/ProductServiceTest.java b/spring/src/test/java/com/jscode/spring/product/service/ProductServiceTest.java index 0e5040b..062455d 100644 --- a/spring/src/test/java/com/jscode/spring/product/service/ProductServiceTest.java +++ b/spring/src/test/java/com/jscode/spring/product/service/ProductServiceTest.java @@ -47,7 +47,7 @@ void saveProduct_success() { void findAll_fail_withInvalidName() { String name = "nothing"; - assertThatThrownBy(() -> productService.findAll(name, null)) + assertThatThrownBy(() -> productService.findAllByName(name, null)) .isInstanceOf(ProductNotFoundException.class) .hasMessageContaining("존재하지 않는 상품입니다."); } @@ -59,7 +59,7 @@ void findAll_success() { ProductResponse productResponse2 = ProductResponse.of(new Product(2L, "키보드", 100_000), 100000); ProductResponse productResponse3 = ProductResponse.of(new Product(3L, "마우스", 50_000), 50000); - ProductListResponse products = productService.findAll(null, null); + ProductListResponse products = productService.findAll( null); Assertions.assertAll( () -> assertThat(products.contains(productResponse1)).isTrue(),