Skip to content

Commit

Permalink
Merge pull request #3 from JSCODE-EDU/HiiWee/day4
Browse files Browse the repository at this point in the history
[이호석] DAY4 Controller, Service, Repository 구분하기(완료)
  • Loading branch information
HiiWee committed Mar 20, 2023
2 parents 1f0b747 + 50cb1cd commit ccc1dc2
Show file tree
Hide file tree
Showing 29 changed files with 754 additions and 2 deletions.
2 changes: 2 additions & 0 deletions spring/.gitignore
@@ -0,0 +1,2 @@
## api key
exchanges.yml
@@ -0,0 +1,9 @@
package com.jscode.spring.advice;

public class BadRequestException extends BusinessException {

public BadRequestException(final String message) {
super(message);
}

}
@@ -0,0 +1,9 @@
package com.jscode.spring.advice;

public class BusinessException extends RuntimeException {

public BusinessException(final String message) {
super(message);
}

}
@@ -0,0 +1,24 @@
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;

/**
* (미션 3 ) 조회/등록 실패할 때 응답 interface 구현
*/
@RestControllerAdvice
public class ControllerAdvice {

@ExceptionHandler(NotFoundException.class)
ResponseEntity<ErrorResponse> handleNotFoundException(NotFoundException e) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(new ErrorResponse(e.getMessage()));
}

@ExceptionHandler(BadRequestException.class)
ResponseEntity<ErrorResponse> handleBadRequestException(BadRequestException e) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(new ErrorResponse(e.getMessage()));
}

}
14 changes: 14 additions & 0 deletions 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;
}

}
@@ -0,0 +1,9 @@
package com.jscode.spring.advice;

public class NotFoundException extends BusinessException {

public NotFoundException(final String message) {
super(message);
}

}
@@ -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;

}
@@ -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);
}

}
@@ -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<String, Double> rates;

public ExchangeRates() {
}

public double findRates(final String countryName) {
return rates.get(countryName);
}

}
@@ -0,0 +1,42 @@
package com.jscode.spring.exchange.service;

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;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

@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 convertKrwTo(final MonetaryUnit monetaryUnit, final int value) {
RestTemplate template = new RestTemplate();
ResponseEntity<ExchangeRates> exchange = template.exchange(
String.format(EXCHANGE_URL, monetaryUnit.toString()),
HttpMethod.GET,
new HttpEntity<>(generateHeader("apikey")),
ExchangeRates.class
);
return value / exchange.getBody()
.findRates("KRW");
}

private HttpHeaders generateHeader(final String header) {
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.set(header, exchangeYamlRead.getKey());
return httpHeaders;
}

}
@@ -0,0 +1,62 @@
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.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;
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
@RestController
@RequestMapping("/api")
public class ProductController {

private final ProductService productService;

public ProductController(final ProductService productService) {
this.productService = productService;
}

/**
* (연습문제 1 ) 상품 등록 api <br>
* (미션 1 ) 상품 등록 api (동일상품 등록시 실패)
*/
@PostMapping("/products")
public ProductSaveResponse saveProduct(@RequestBody final NewProductRequest newProductRequest) {
Long generatedId = productService.saveProduct(newProductRequest);
return new ProductSaveResponse(generatedId);
}

/**
* (연습문제 2 ) 상품 조회 api
*/
@GetMapping("/products/{productId}")
public ProductResponse findProductById(@PathVariable final Long productId,
@RequestParam @Nullable final String monetaryUnit) {
return productService.findProductById(productId, monetaryUnit);
}

/**
* (연습문제 3 ) 전체 상품 조회 (미션) 상품 이름으로 상세조회하는 api <br>
* (미션 2 ) 상품 상세 조회 api
*/
@GetMapping("/products")
public ProductListResponse findProducts(@RequestParam @Nullable final String name,
@RequestParam @Nullable final String monetaryUnit) {
if (Objects.isNull(name)) {
return productService.findAll(monetaryUnit);
}
return productService.findAllByName(name, monetaryUnit);
}

}
@@ -0,0 +1,5 @@
package com.jscode.spring.product.domain;

public enum MonetaryUnit {
USD, KRW
}
30 changes: 30 additions & 0 deletions spring/src/main/java/com/jscode/spring/product/domain/Product.java
@@ -0,0 +1,30 @@
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 isSameId(final Long id) {
return Objects.equals(this.id, id);
}

public boolean isSameName(final String name) {
return this.name.equals(name);
}
}
@@ -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;

private NewProductRequest() {
}

public NewProductRequest(final String name, final int price) {
this.name = name;
this.price = price;
}

public Product toDomain() {
return new Product(null, name, price);
}

}
@@ -0,0 +1,19 @@
package com.jscode.spring.product.dto;

import java.util.List;
import lombok.Getter;

@Getter
public class ProductListResponse {

private List<ProductResponse> productResponses;

public ProductListResponse(final List<ProductResponse> productResponses) {
this.productResponses = productResponses;
}

public boolean contains(final ProductResponse productResponse) {
return productResponses.contains(productResponse);
}

}
@@ -0,0 +1,41 @@
package com.jscode.spring.product.dto;

import com.jscode.spring.product.domain.Product;
import java.util.Objects;
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 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);
}

}
@@ -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;
}

}
@@ -0,0 +1,13 @@
package com.jscode.spring.product.exception;

import com.jscode.spring.advice.BadRequestException;

public class DuplicateNameException extends BadRequestException {

private static final String MESSAGE = "동일한 이름의 상품은 저장할 수 없습니다.";

public DuplicateNameException() {
super(MESSAGE);
}

}

0 comments on commit ccc1dc2

Please sign in to comment.