Skip to content

Commit

Permalink
Merge branch 'weekly' into weekly
Browse files Browse the repository at this point in the history
  • Loading branch information
yooonwodyd authored Nov 7, 2024
2 parents 6298063 + f01fcb6 commit 1a7da80
Show file tree
Hide file tree
Showing 26 changed files with 348 additions and 91 deletions.
18 changes: 8 additions & 10 deletions .github/workflows/main.yml → .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Java CI
name: Java CICD

on:
workflow_dispatch:
Expand All @@ -23,18 +23,18 @@ jobs:
- name: Create application.yaml
run: |
mkdir -p src/main/resources
echo "${{ secrets.APPLICATION_YAML }}" > src/main/resources/application.yaml
echo '${{ secrets.APPLICATION_YAML }}' > src/main/resources/application.yaml
cat src/main/resources/application.yaml
shell: bash

- name: Grant execute permission for Gradle Wrapper
run: chmod +x ./gradlew

- name: Build with Gradle
run: ./gradlew build
run: ./gradlew clean build -x test

- name: Test with Gradle
run: ./gradlew test
# - name: Test with Gradle
# run: ./gradlew test

# 경로 확인
- name: Verify JAR file
Expand Down Expand Up @@ -66,8 +66,6 @@ jobs:
key: ${{ secrets.AWS_EC2_PRIVATE_KEY }} # EC2 인스턴스 pem key
port: ${{ secrets.REMOTE_SSH_PORT }} # 접속 포트(생략 시 22번 기본 사용)
script: |
docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }}
docker pull ${{ secrets.DOCKER_USERNAME }}/katecam-backend:latest
docker stop katecam-backend
docker rm $(docker ps --filter 'status=exited' -a -q)
docker run -d --name katecam-backend --network katecam-backend --log-driver=syslog -p 8080:8080 ${{ secrets.DOCKER_USERNAME }}/katecam-backend:latest
cd /home/ubuntu # EC2 인스턴스의 배포 스크립트 파일 경로로 이동
chmod +x deploy.sh # 배포 스크립트 실행 권한 부여
./deploy.sh # 배포 스크립트 실행
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,15 @@

## 리뷰사항
연휴 이후 다음주 부터 본격적인 개발에 들어가려 합니다. 열심히하겠습니다! 감사합니다.



# 5주차 github 코드리뷰 질문
(윤재용)
몇몇 컨트롤러에 대한 E2E 테스트를 작성하였습니다.
처음에는 @WithMockUser 를 사용해서 테스트를 진행하려고 했는데, Header를 검증하다보니 불가능했습니다.
저희의 요구사항이 특정 url이 아니라면 헤더에 토큰이 필요하다보니 사용이 불가능하였기에 JwtTestUtils 클래스를 통해 테스트 유저를 사용하였습니다.

이런 전체 테스트를 처음 구현하다 보니
현재 작성한 테스트가 E2E 테스트라고 불려도 될 지 잘 모르겠습니다..!
추가로 테스트코드의 개선점이 있을지 궁금합니다.
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.helpmeCookies.global.ApiResponse;

import jakarta.annotation.Nullable;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;

@Getter
@AllArgsConstructor
@RequiredArgsConstructor
public class ApiResponse<T> {

private final int code;
private final String message;
private T data;

public static<T> ApiResponse<T> success(SuccessCode successCode, T data) {
return new ApiResponse<T>(successCode.getCode(), successCode.getMessage(),data);
}

public static ApiResponse<Void> success(SuccessCode successCode) {
return new ApiResponse<>(successCode.getCode(), successCode.getMessage(),null);
}

public static ApiResponse<Void> error(HttpStatus errorCode, String message) {
return new ApiResponse<>(errorCode.value(), message,null);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.helpmeCookies.global.ApiResponse;

import lombok.AllArgsConstructor;
import lombok.Getter;
import org.springframework.http.HttpStatus;

@AllArgsConstructor
@Getter
public enum SuccessCode {
OK(200, "OK"),
CREATED_SUCCESS(201, "저장에 성공했습니다");

private final int code;
private final String message;

}
19 changes: 19 additions & 0 deletions src/main/java/com/helpmeCookies/global/config/WebConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.helpmeCookies.global.config;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
@RequiredArgsConstructor
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOriginPatterns("*") // 허용할 도메인 (모든 도메인 허용: "*")
.allowedMethods("GET", "POST", "PUT", "PATCH", "DELETE") // 허용할 HTTP 메서드
.allowedHeaders("*") // 허용할 헤더
.allowCredentials(true); // 인증 정보 허용 여부
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package com.helpmeCookies.global.exception;

import com.helpmeCookies.global.ApiResponse.ApiResponse;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

Expand All @@ -10,8 +13,8 @@
public class GlobalExceptionHandler {

@ExceptionHandler(ResourceNotFoundException.class)
public String handleResourceNotFoundException() {
return "해당 리소스를 찾을 수 없습니다.";
public ResponseEntity<ApiResponse<Void>> handleResourceNotFoundException() {
return ResponseEntity.badRequest().body(ApiResponse.error(HttpStatus.BAD_REQUEST,"해당 리소스를 찾을 수 없습니다."));
}

@ExceptionHandler(DuplicateRequestException.class)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.helpmeCookies.global.infra;

import com.helpmeCookies.user.repository.UserRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.stereotype.Component;

@Component
@RequiredArgsConstructor
public class CustomDatabaseHealthIndicator implements HealthIndicator {

private final UserRepository userRepository;
@Override
public Health health() {
try {
// 테이블이 존재하는지 확인하는 쿼리 (Users 테이블 사용)
long count = userRepository.count();
// 테이블 존재 시 0 이상을 반환
return Health.up().withDetail("Users table exists, count: ", count).build();
} catch (Exception e) {
// 테이블이 없거나 데이터베이스 연결에 문제가 있는 경우
return Health.down(e).withDetail("Users table", "Table missing or database issue")
.build();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
public class UserDetailService {
private final UserRepository userRepository;


public JwtUser loadUserByEmail(String email,String nickname) throws UsernameNotFoundException {
// 만약 유저가 존재하지 않는다면 저장
User user = userRepository.findByUserInfoEmail(email)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
"/login", "/signup", "/ttt/*", "/user",
"/api/auth/**",
"/swagger-ui/**",
"/swagger-resources",
"/v3/api-docs/**",
"/actuator/**",
"/v1/**",
"swagger-ui/**",
Expand Down
32 changes: 13 additions & 19 deletions src/main/java/com/helpmeCookies/global/utils/AwsS3FileUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import com.amazonaws.services.s3.model.CannedAccessControlList;
import com.amazonaws.services.s3.model.ObjectMetadata;
import com.amazonaws.services.s3.model.PutObjectRequest;
import com.helpmeCookies.product.dto.FileUploadResponse;
import com.helpmeCookies.product.dto.ImageUpload;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
Expand All @@ -14,7 +14,6 @@

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
Expand All @@ -28,26 +27,21 @@ public class AwsS3FileUtils {
private String bucket;

//다중파일 업로드후 url 반환
public List<FileUploadResponse> uploadMultiImages(List<MultipartFile> multipartFiles) {
List<FileUploadResponse> fileList = new ArrayList<>();
public ImageUpload uploadMultiImages(MultipartFile multipartFile) {

multipartFiles.forEach(file -> {
String fileName = createFileName(file.getOriginalFilename()); //파일 이름 난수화
ObjectMetadata objectMetadata = new ObjectMetadata();
objectMetadata.setContentLength(file.getSize());
objectMetadata.setContentType(file.getContentType());
String fileName = createFileName(multipartFile.getOriginalFilename()); //파일 이름 난수화
ObjectMetadata objectMetadata = new ObjectMetadata();
objectMetadata.setContentLength(multipartFile.getSize());
objectMetadata.setContentType(multipartFile.getContentType());

try (InputStream inputStream = file.getInputStream()) {
amazonS3.putObject(new PutObjectRequest(bucket, fileName, inputStream, objectMetadata)
.withCannedAcl(CannedAccessControlList.PublicRead));
} catch (IOException e) {
throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "파일 업로드 실패" + fileName);
}

fileList.add(new FileUploadResponse(amazonS3.getUrl(bucket,fileName).toString(),fileName));
});
try (InputStream inputStream = multipartFile.getInputStream()) {
amazonS3.putObject(new PutObjectRequest(bucket, fileName, inputStream, objectMetadata)
.withCannedAcl(CannedAccessControlList.PublicRead));
} catch (IOException e) {
throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "파일 업로드 실패" + fileName);
}

return fileList;
return new ImageUpload(amazonS3.getUrl(bucket,fileName).toString());
}

public String createFileName(String fileName) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package com.helpmeCookies.product.controller;

import com.helpmeCookies.global.ApiResponse.ApiResponse;
import com.helpmeCookies.global.ApiResponse.SuccessCode;
import com.helpmeCookies.product.dto.ImageUpload;
import static com.helpmeCookies.product.util.SortUtil.convertProductSort;

import com.helpmeCookies.product.controller.docs.ProductApiDocs;
import com.helpmeCookies.product.dto.FileUploadResponse;
import com.helpmeCookies.product.dto.ProductImageResponse;
import com.helpmeCookies.product.dto.ProductPage;
import com.helpmeCookies.product.dto.ProductRequest;
Expand All @@ -19,7 +21,6 @@
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.util.List;

@RestController
Expand All @@ -30,22 +31,29 @@ public class ProductController implements ProductApiDocs {
private final ProductService productService;
private final ProductImageService productImageService;

@PostMapping("/successTest")
public ResponseEntity<ApiResponse<Void>> saveTest() {
return ResponseEntity.ok(ApiResponse.success(SuccessCode.OK));
}

@PostMapping
public ResponseEntity<Void> saveProduct(@RequestBody ProductRequest productRequest) {
productService.save(productRequest);
Product product = productService.save(productRequest);
productImageService.saveImages(product.getId(),productRequest.imageUrls());
return ResponseEntity.ok().build();
}

@PostMapping("/{productId}/images")
public ResponseEntity<ProductImageResponse> uploadImages(@PathVariable("productId") Long productId, List<MultipartFile> files) throws IOException {
List<FileUploadResponse> responses = productImageService.uploadMultiFiles(productId,files);
return ResponseEntity.ok(new ProductImageResponse(responses.stream().map(FileUploadResponse::photoUrl).toList()));
@PostMapping("/images")
public ResponseEntity<ProductImageResponse> uploadImages(List<MultipartFile> files) {
List<ImageUpload> responses = productImageService.uploadMultiFiles(files);
return ResponseEntity.ok(new ProductImageResponse(responses.stream().map(ImageUpload::photoUrl).toList()));
}

@GetMapping("/{productId}")
public ResponseEntity<ProductResponse> getProductInfo(@PathVariable("productId") Long productId) {
Product product = productService.find(productId);
return ResponseEntity.ok(ProductResponse.from(product));
List<String> urls = productImageService.getImages(productId);
return ResponseEntity.ok(ProductResponse.from(product,urls));
}

@PutMapping("/{productId}")
Expand All @@ -56,8 +64,11 @@ public ResponseEntity<Void> editProductInfo(@PathVariable("productId") Long prod
}

@PutMapping("/{productId}/images")
public ResponseEntity<Void> editImages(@PathVariable("productId") Long productId, List<MultipartFile> files) throws IOException {
public ResponseEntity<Void> editImages(@PathVariable("productId") Long productId, List<MultipartFile> files) {
productImageService.editImages(productId, files);
List<String> images = productImageService.uploadMultiFiles(files).stream()
.map(ImageUpload::photoUrl).toList();
productImageService.saveImages(productId,images);
return ResponseEntity.ok().build();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,13 @@

import com.helpmeCookies.product.entity.ProductImage;

public record FileUploadResponse(
String photoUrl,
String uuid
public record ImageUpload(
String photoUrl
) {
public ProductImage toEntity(Long productId) {
return ProductImage.builder()
.productId(productId)
.photoUrl(photoUrl)
.uuid(uuid)
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ public record ProductRequest(
String description,
String preferredLocation,
List<HashTag> hashTags,
Long artistInfo
Long artistInfoId,
List<String> imageUrls

) {
public Product toEntity(ArtistInfo artistInfo) {
return Product.builder()
Expand Down
28 changes: 6 additions & 22 deletions src/main/java/com/helpmeCookies/product/dto/ProductResponse.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,28 +14,13 @@ public record ProductResponse(
String description,
String preferredLocation,
List<HashTag> hashTags,
ArtistInfo artistInfo
ArtistInfo artistInfo,
List<String> imageUrls
) {
public static class ArtistInfo {
private final Long artistId;
private final String name;

public ArtistInfo(Long artistId, String name) {
this.artistId = artistId;
this.name = name;
}

public Long getArtistId() {
return artistId;
}

public String getName() {
return name;
}
public record ArtistInfo(Long artistId, String artistName) {
}

public static ProductResponse from(Product product) {
//TODO artistInfo 코드 개발 이후 수정 예정
public static ProductResponse from(Product product, List<String> urls) {
return new ProductResponse(
product.getId(),
product.getName(),
Expand All @@ -45,9 +30,8 @@ public static ProductResponse from(Product product) {
product.getDescription(),
product.getPreferredLocation(),
product.getHashTags(),
new ArtistInfo(
1L, "임시"
)
new ArtistInfo(product.getArtistInfo().getId(),product.getArtistInfo().getNickname()),
urls
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,9 @@ public ProductImage(String photoUrl, Long productId, String uuid) {
this.productId = productId;
this.uuid = uuid;
}

public String getPhotoUrl() {
return photoUrl;
}
}

Loading

0 comments on commit 1a7da80

Please sign in to comment.