Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

10주차 PR (Develop <- Weekly/10) #93

Merged
merged 19 commits into from
Nov 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
4d90b1a
[Weekly/10/Test/Member-Service] 멤버 도메인 서비스 유닛테스트 (#70)
lja3723 Nov 2, 2024
a72b1fc
[Weekly/10/All/fetchJoin] 연관관계 설정 수정, 페치 조인, 배치사이즈 설정, 페이지네이션 (#71)
Daolove0323 Nov 5, 2024
f5d4acd
[Weekly/10/All/Set-Authorization] authorization 로직 추가 (#72)
Daolove0323 Nov 5, 2024
2bcc318
[Weekly/10/Feature/Authorization] 모든 컨트롤러 authorization 적용 및 서비스 검증 로…
Daolove0323 Nov 6, 2024
1c4f47e
[Weekly/10/Deploy/CICD] CI/CD 관련 스크립트 추가 (#74)
lja3723 Nov 6, 2024
49dd8b4
[Weekly/10/Test/Event] Event 테스트(1차) 및 TestData 리펙터링 (#77)
ariimo Nov 7, 2024
91f27bd
[Weekly/10/Chore/Deploy] CI/CD 스크립트 그룹화 (#78)
lja3723 Nov 7, 2024
dd79cee
[Weekly/10/Test/Authorization] 큐레이션 유닛 테스트 (#79)
Daolove0323 Nov 7, 2024
0007a37
[Weekly/10/Test/Event] Event 단위테스트 (#80)
ariimo Nov 7, 2024
af4b98a
[Weekly/10/Chore/Deploy] PR시 secrets에 접근 가능하도록 수정 (#81)
lja3723 Nov 7, 2024
f4f82f4
refactor: Like Controller return값 리팩터링 (#82)
ariimo Nov 7, 2024
d3fdabe
[Weekly/10/Chore/Deploy] push일 때만 Deploy되도록 수정 (#83)
lja3723 Nov 7, 2024
a46924a
[Weekly/10/Test/Member-Controllers] add MemberControllerUnitTest Temp…
lja3723 Nov 7, 2024
3db4d86
feat: 큐레이션 컨트롤러 유닛테스트 (#85)
Daolove0323 Nov 8, 2024
34b4ca9
[Weekly/10/Test/Refactor-MemberId] test 관련 member 도메인의 아이디 상수화 이외 1개 …
lja3723 Nov 8, 2024
42a65ce
[Weekly/10/Chore/Deploy] Github Action 스크립트 수정 (#89)
lja3723 Nov 8, 2024
3ffa4b1
[Weekly/10/Test/CurationController] curationControllerUnitTest (#88)
Daolove0323 Nov 8, 2024
1a1d50f
[Weekly/10/Test/CurationController] curationControllerUnitTest 2 (#90)
Daolove0323 Nov 8, 2024
9e96a24
[Weekly/10/Reservation/unitTest] Reservation unit 테스트 추가
Daolove0323 Nov 8, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
188 changes: 188 additions & 0 deletions .github/workflows/gradle.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
name: CI/CD Github Action

on:
push:
branches: [ "Master", "Weekly/*" ]
pull_request_target:
branches: [ "Master", "Weekly/*" ]

permissions:
contents: read

env:
GOOGLE_CLIENT_ID : ${{ secrets.ENV_GOOGLE_CLIENT_ID }}
GOOGLE_CLIENT_SECRET: ${{ secrets.ENV_GOOGLE_CLIENT_SECRET }}
GOOGLE_REDIRECT_URI : ${{ secrets.ENV_GOOGLE_REDIRECT_URI }}
JWT_SECRET : ${{ secrets.ENV_JWT_SECRET }}
KAKAO_CLIENT_ID : ${{ secrets.ENV_KAKAO_CLIENT_ID }}
KAKAO_CLIENT_SECRET : ${{ secrets.ENV_KAKAO_CLIENT_SECRET }}
KAKAO_REDIRECT_URI : ${{ secrets.ENV_KAKAO_REDIRECT_URI }}
KAKAOPAY_SECRET_KEY : ${{ secrets.ENV_KAKAOPAY_SECRET_KEY }}

jobs:
## 1단계: 프로젝트 빌드
Build:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4

- name: Set up JDK 21
uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'corretto'

- name: Gradle Caching (for faster build)
uses: actions/cache@v3
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
restore-keys: |
${{ runner.os }}-gradle-

- name: Build Application
run: |
chmod +x gradlew
./gradlew clean build -x test

- name: Store build failure reports (execute when build fail)
if: failure()
uses: actions/upload-artifact@v3
with:
name: build-failure-reports
path: |
**/build/reports/

- name: Store build artifacts
uses: actions/upload-artifact@v3
with:
name: build-artifacts
path: build/libs/*.jar

## 2단계: 테스트 실행
Test:
runs-on: ubuntu-22.04
needs: Build # Build 단계가 완료되어야 실행됨
steps:
- uses: actions/checkout@v4

- name: Set up JDK 21
uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'corretto'

- name: Gradle Caching (for faster build)
uses: actions/cache@v3
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
restore-keys: |
${{ runner.os }}-gradle-

- name: Test Application
run: |
chmod +x gradlew
./gradlew test

- name: Store test failure reports (execute when test fail)
if: failure()
uses: actions/upload-artifact@v3
with:
name: test-failure-reports
path: |
**/build/test-results/

## 3단계: Docker 빌드 및 푸시
Docker-Build:
runs-on: ubuntu-22.04
needs: Test # Test 단계가 성공적으로 완료되어야 실행됨
if: github.event_name == 'push'
steps:
- uses: actions/checkout@v4

- name: Download Build Artifacts
uses: actions/download-artifact@v3
with:
name: build-artifacts
path: build/libs

- name: Docker Hub Login
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_TOKEN }}

- name: Docker Build
run: docker build -f Dockerfile --build-arg DEPENDENCY=build/dependency -t ${{ secrets.DOCKER_REPO_FULLNAME }} .

- name: Docker Push
run: docker push ${{ secrets.DOCKER_REPO_FULLNAME }}

## 4단계: 서버에 배포
Deploy:
runs-on: ubuntu-22.04
needs: Docker-Build
if: github.event_name == 'push'
steps:
- name: Update .env
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.SSH_HOST }}
username: ${{ secrets.SSH_USERNAME }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
port: ${{ secrets.SSH_PORT }}
script: |
echo "GOOGLE_CLIENT_ID=${{ secrets.ENV_GOOGLE_CLIENT_ID }}" > ~/.env
echo "GOOGLE_CLIENT_SECRET=${{ secrets.ENV_GOOGLE_CLIENT_SECRET }}" >> ~/.env
echo "GOOGLE_REDIRECT_URI=${{ secrets.ENV_GOOGLE_REDIRECT_URI }}" >> ~/.env
echo "JWT_SECRET=${{ secrets.ENV_JWT_SECRET }}" >> ~/.env
echo "KAKAO_CLIENT_ID=${{ secrets.ENV_KAKAO_CLIENT_ID }}" >> ~/.env
echo "KAKAO_CLIENT_SECRET=${{ secrets.ENV_KAKAO_CLIENT_SECRET }}" >> ~/.env
echo "KAKAO_REDIRECT_URI=${{ secrets.ENV_KAKAO_REDIRECT_URI }}" >> ~/.env
echo "KAKAOPAY_SECRET_KEY=${{ secrets.ENV_KAKAOPAY_SECRET_KEY }}" >> ~/.env
echo "Environment setup has been completed."

- name: Pull New Docker Image
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.SSH_HOST }}
username: ${{ secrets.SSH_USERNAME }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
port: ${{ secrets.SSH_PORT }}
envs: GITHUB_SHA
script: sudo docker pull ${{ secrets.DOCKER_REPO_FULLNAME }}

- name: Stop Old Docker Image
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.SSH_HOST }}
username: ${{ secrets.SSH_USERNAME }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
port: ${{ secrets.SSH_PORT }}
envs: GITHUB_SHA
script: sudo docker stop would-you-in

- name: Run New Docker Image
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.SSH_HOST }}
username: ${{ secrets.SSH_USERNAME }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
port: ${{ secrets.SSH_PORT }}
envs: GITHUB_SHA
script: sudo docker run --rm -d -p 80:8080 --env-file ~/.env --name would-you-in ${{ secrets.DOCKER_REPO_FULLNAME }}

- name: Clean-Up Docker Image
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.SSH_HOST }}
username: ${{ secrets.SSH_USERNAME }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
port: ${{ secrets.SSH_PORT }}
envs: GITHUB_SHA
script: sudo docker image prune -f
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,6 @@ out/

### static images
/src/main/resources/staticimages/

### env file
.env
12 changes: 12 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# open jdk 21 버전의 환경을 구성한다.
FROM openjdk:21-jdk-slim

# build가 될 때 JAR_FILE이라는 변수 명에 build/libs/*.jar 선언
# build/libs - gradle로 빌드했을 때 jar 파일이 생성되는 경로임
ARG JAR_FILE=build/libs/*.jar

# JAR_FILE을 agaproject.jar로 복사 (이 부분(.jar)은 개발환경에 따라 다름)
COPY ${JAR_FILE} wouldyouin.jar

# 운영 및 개발에서 사용되는 환경 설정을 분리한다.
ENTRYPOINT ["java", "-jar", "-Dspring.profiles.active=deploy", "/wouldyouin.jar"]
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,13 @@
import lombok.RequiredArgsConstructor;
import org.ktc2.cokaen.wouldyouin.Image.api.dto.ImageResponse;
import org.ktc2.cokaen.wouldyouin.Image.application.ImageServiceFactory;
import org.ktc2.cokaen.wouldyouin.Image.application.ImageStorage;
import org.ktc2.cokaen.wouldyouin._common.api.ApiResponse;
import org.ktc2.cokaen.wouldyouin._common.api.ApiResponseBody;
import org.ktc2.cokaen.wouldyouin._common.exception.FailToReadImageException;
import org.ktc2.cokaen.wouldyouin.auth.Authorize;
import org.ktc2.cokaen.wouldyouin.auth.MemberIdentifier;
import org.ktc2.cokaen.wouldyouin.member.persist.MemberType;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
Expand All @@ -29,8 +33,10 @@ public class ImageController {

private final ImageServiceFactory imageServiceFactory;

// Todo: authorize
@PostMapping("/images")
public ResponseEntity<ApiResponseBody<List<ImageResponse>>> uploadImages(@RequestParam List<MultipartFile> images,
public ResponseEntity<ApiResponseBody<List<ImageResponse>>> uploadImages(
@RequestParam List<MultipartFile> images,
@RequestParam(value = "type") ImageDomain imageDomain) {
return ApiResponse.ok(imageServiceFactory.getImageServiceByImageType(imageDomain).saveAndCreateImages(images));
}
Expand All @@ -44,8 +50,11 @@ public ResponseEntity<byte[]> getImage(@PathVariable String path) {
}
}

// Todo: authorize
@DeleteMapping("/images/{id}")
public ResponseEntity<ApiResponseBody<Void>> deleteImage(@PathVariable Long id, @RequestParam ImageDomain imageDomain) {
public ResponseEntity<ApiResponseBody<Void>> deleteImage(
@PathVariable Long id,
@RequestParam(value = "type") ImageDomain imageDomain) {
imageServiceFactory.getImageServiceByImageType(imageDomain).deleteAndDelete(id);
return ApiResponse.noContent();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,11 @@ public class ImageResponse {
private LocalDateTime createdDate;
private Long size;

public static ImageResponse from(Image image) {
public static ImageResponse from(Image image, String domainName) {
System.out.println("도메인" + domainName);
return ImageResponse.builder()
.id(image.getId())
.url(image.getUrl())
.url(domainName + image.getUrl())
.size(image.getSize())
.createdDate(image.getCreatedDate())
.build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@
@RequiredArgsConstructor
public class AdvertisementImageService extends ImageService<AdvertisementImage> {

@Value("${image.upload.ad.sub-path}")
private String subPath;
private final ImageStorage imageStorage;
private final AdvertisementImageRepository adImageRepository;
@Value("${image.upload.ad.sub-path}")
private String subPath;

@Override
protected ImageRepository<AdvertisementImage> getImageRepository() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ protected String getSubPath() {
@Override
protected CurationImage toEntity(ImageRequest imageRequest) {
return CurationImage.builder()
.name(imageRequest.getUrl())
.url(imageRequest.getUrl())
.size(imageRequest.getSize())
.build();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,13 @@
package org.ktc2.cokaen.wouldyouin.Image.application;

import java.util.List;
import java.util.Optional;
import lombok.RequiredArgsConstructor;
import org.ktc2.cokaen.wouldyouin.Image.api.ImageDomain;
import org.ktc2.cokaen.wouldyouin.Image.api.dto.ImageRequest;
import org.ktc2.cokaen.wouldyouin.Image.persist.CurationImage;
import org.ktc2.cokaen.wouldyouin.Image.persist.EventImage;
import org.ktc2.cokaen.wouldyouin.Image.persist.EventImageRepository;
import org.ktc2.cokaen.wouldyouin.Image.persist.ImageRepository;
import org.ktc2.cokaen.wouldyouin._common.exception.EntityNotFoundException;
import org.ktc2.cokaen.wouldyouin._common.exception.EntityParamIsNullException;
import org.ktc2.cokaen.wouldyouin.advertisement.persist.Advertisement;
import org.ktc2.cokaen.wouldyouin.curation.persist.CurationCard;
import org.ktc2.cokaen.wouldyouin.event.persist.Event;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
Expand Down Expand Up @@ -44,7 +39,7 @@ protected String getSubPath() {

@Override
protected EventImage toEntity(ImageRequest imageRequest) {
return EventImage.builder().name(imageRequest.getUrl()).size(imageRequest.getSize()).build();
return EventImage.builder().url(imageRequest.getUrl()).size(imageRequest.getSize()).build();
}

@Transactional
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import org.ktc2.cokaen.wouldyouin.Image.persist.ImageRepository;
import org.ktc2.cokaen.wouldyouin._common.exception.EntityNotFoundException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
Expand All @@ -20,6 +21,9 @@ public abstract class ImageService<T extends Image> {
@Autowired
protected ImageStorage imageStorage;

@Value("${spring.wouldyouin-domain-name}")
private String domainName;

protected abstract ImageRepository<T> getImageRepository();

protected abstract ImageDomain getImageDomain();
Expand All @@ -28,13 +32,13 @@ public abstract class ImageService<T extends Image> {

protected abstract T toEntity(ImageRequest imageRequest);

public T getByIdOrThrow(Long id) {
public T getById(Long id) {
return getImageRepository().findById(id)
.orElseThrow(() -> new EntityNotFoundException(getImageDomain().name() + " Image"));
}

protected ImageResponse create(ImageRequest imageRequest) {
return ImageResponse.from(getImageRepository().save(toEntity(imageRequest)));
return ImageResponse.from(getImageRepository().save(toEntity(imageRequest)), domainName);
}

protected void delete(Long id) {
Expand Down
Loading
Loading