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

feat: 특정 방문 기록 삭제 API 구현 #26 #42

Merged
merged 22 commits into from
Jul 24, 2024
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
295c97c
feat: 특정 방문 기록 삭제 API 구현
devhoya97 Jul 22, 2024
5eefdf6
feat: 양수가 아닌 id로 특정 방문 기록 삭제를 시도할 때 예외 처리 기능 구현
devhoya97 Jul 22, 2024
e03d7d5
feat: 방문 기록 삭제 시 방문 로그도 함께 삭제되는 기능 구현
devhoya97 Jul 22, 2024
30a2fc3
refactor: 커스텀 예외를 제거하는 방향으로 변경
devhoya97 Jul 23, 2024
8b9914c
fix: 예외를 못 잡던 문제 해결
devhoya97 Jul 23, 2024
2748c25
refactor: 메서드명 적절하게 변경
devhoya97 Jul 23, 2024
939d46d
build: Docker Compose Setting #27 (#40)
Ho-Tea Jul 22, 2024
b735d14
feat: 여행 상세 생성 API 구현 #18 (#43)
linirini Jul 23, 2024
60171c8
build: CD yml 파일 구성 #28 (#53)
Ho-Tea Jul 23, 2024
3bf8c9b
fix: rebase 과정에서 파일이 꼬인 문제 해결
devhoya97 Jul 24, 2024
495f98e
test: HttpHeaders.AUTHORIZATION 사용
devhoya97 Jul 24, 2024
c2bfc63
refactor: 중간 테이블 엔티티 수정 #56 (#57)
linirini Jul 24, 2024
c490e2c
feat: Pin, Visit, VisitLog 생성자에 builder 추가
devhoya97 Jul 24, 2024
6a94854
feat: Pin repository 추가
devhoya97 Jul 24, 2024
f144a77
refactor: visit이 삭제되기 전에 visit에 의존하는 visitLog들이 먼저 삭제되도록 순서 변경
devhoya97 Jul 24, 2024
076e570
test: 방문 기록 삭제에 대한 서비스 슬라이스 테스트 추가
devhoya97 Jul 24, 2024
a559d68
test: 방문 기록이 갖는 모든 방문 로그 삭제 메서드 테스트
devhoya97 Jul 24, 2024
665ea5f
fix: Modifying을 사용할 때 영속성컨텍스트와 관련하여 발생하던 문제 해결
devhoya97 Jul 24, 2024
55218fd
refactor: visitLog의 content를 필수값으로 변경
devhoya97 Jul 24, 2024
94d2664
test: 컨벤션에 맞게 Controller 테스트 클래스 변경
devhoya97 Jul 24, 2024
fff5c00
fix: ConstraintViolationException의 예외 메시지를 정해둔 형식에 맞게 변경
devhoya97 Jul 24, 2024
4ad9148
Merge branch 'develop-be' into feature/#26-delete-visit
devhoya97 Jul 24, 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
59 changes: 59 additions & 0 deletions .github/workflows/backend-ci-cd-dev.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
name: Backend CI/CD dev

on:
push:
branches: [ "develop-be" ]

jobs:
ci:
runs-on: ubuntu-latest

defaults:
run:
shell: bash
working-directory: ./backend

permissions:
contents: read

steps:
- uses: actions/checkout@v4
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'

- name: Grant execute permission for gradlew
run: chmod +x gradlew

- name: Setup with Gradle
uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5 # v3.1.0

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

- name: Login to Docker Hub
uses: docker/[email protected]
with:
username: ${{ secrets.DOCKERHUB_DEPLOY_USERNAME }}
password: ${{ secrets.DOCKERHUB_DEPLOY_TOKEN }}

- name: Docker Image Build
run: |
docker build --platform linux/arm64 -t hotea990/staccato -f Dockerfile .

- name: Docker Hub Push
run: docker push hotea990/staccato

cd:
needs: ci
runs-on: self-hosted
steps:
- name: Pull Docker image
run: |
sudo docker login --username ${{ secrets.DOCKERHUB_DEPLOY_USERNAME }} --password ${{ secrets.DOCKERHUB_DEPLOY_TOKEN }}
sudo docker pull hotea990/staccato

- name: Docker Compose up
run: sudo docker-compose -f /home/ubuntu/staccato/docker-compose.yml up -d
7 changes: 7 additions & 0 deletions backend/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.env

.idea

.gradle

mysql
5 changes: 5 additions & 0 deletions backend/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
FROM openjdk:17
EXPOSE 8080
ARG JAR_FILE=./build/libs/*-SNAPSHOT.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java", "-jar","-Dspring.profiles.active=dev","app.jar"]
4 changes: 4 additions & 0 deletions backend/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,12 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
compileOnly 'org.projectlombok:lombok'
runtimeOnly 'com.h2database:h2'
runtimeOnly 'com.mysql:mysql-connector-j'
annotationProcessor 'org.projectlombok:lombok'

testImplementation 'io.rest-assured:rest-assured:5.3.1'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'io.rest-assured:rest-assured:5.3.1'
Copy link
Contributor

@BurningFalls BurningFalls Jul 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

API 테스트를 위해서 RestAssured 라이브러리를 추가하셨네요! 동일하게 수정이 필요한 build.gradle의 변경 사항이라서, 코드를 합칠 때 모두가 신경써야 할 부분이라고 생각합니다.

  • 라이브러리 사용 여부
  • 라이브러리 선언 위치
  • 라이브러리 버전

Copy link
Contributor Author

@devhoya97 devhoya97 Jul 24, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 문제는 충돌이 발생하면 해결해야겠군요!

testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}

Expand Down
35 changes: 35 additions & 0 deletions backend/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
version: "3.8"
services:
database:
container_name: staccato-database
image: mysql:8.0.30
environment:
- MYSQL_DATABASE=staccato
- MYSQL_USER=${MYSQL_USER}
- MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}
- MYSQL_PASSWORD=${MYSQL_PASSWORD}
volumes:
- ./mysql:/var/lib/mysql
ports:
- "3306:3306"
restart: always
networks:
- springboot-mysql-network
application:
container_name: staccato-backend-app
image: ${STACCATO_IMAGE}
depends_on:
- database
environment:
- SPRING_DATASOURCE_URL=${SPRING_DATASOURCE_URL}
- SPRING_DATASOURCE_USERNAME=${SPRING_DATASOURCE_USERNAME}
- SPRING_DATASOURCE_PASSWORD=${SPRING_DATASOURCE_PASSWORD}
ports:
- "80:8080"
restart: always
networks:
- springboot-mysql-network

networks:
springboot-mysql-network:
driver: bridge
6 changes: 3 additions & 3 deletions backend/src/main/java/com/staccato/StaccatoApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
@SpringBootApplication
public class StaccatoApplication {

public static void main(String[] args) {
SpringApplication.run(StaccatoApplication.class, args);
}
public static void main(String[] args) {
SpringApplication.run(StaccatoApplication.class, args);
}

}
17 changes: 17 additions & 0 deletions backend/src/main/java/com/staccato/config/WebMvcConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.staccato.config;

import java.util.List;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import com.staccato.config.auth.MemberIdArgumentResolver;

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(new MemberIdArgumentResolver());
}
}
11 changes: 11 additions & 0 deletions backend/src/main/java/com/staccato/config/auth/MemberId.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.staccato.config.auth;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface MemberId {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.staccato.config.auth;

import jakarta.servlet.http.HttpServletRequest;

import org.springframework.core.MethodParameter;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;

@Component
public class MemberIdArgumentResolver implements HandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(MemberId.class);
}

@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) {
HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest();
return Long.parseLong(request.getHeader(HttpHeaders.AUTHORIZATION));
}
}
Original file line number Diff line number Diff line change
@@ -1,17 +1,49 @@
package com.staccato.exception;

import java.time.format.DateTimeParseException;
import java.util.Optional;

import jakarta.validation.ConstraintViolationException;

import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(DateTimeParseException.class)
public ResponseEntity<ExceptionResponse> handleDateTimeParseException(DateTimeParseException e) {
public ResponseEntity<ExceptionResponse> handleDateTimeParseException() {
return ResponseEntity.badRequest()
.body(new ExceptionResponse(HttpStatus.BAD_REQUEST.toString(), "올바르지 않은 날짜 형식입니다."));
}

@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ExceptionResponse> handleValidationException(MethodArgumentNotValidException e) {
String message = Optional.ofNullable(e.getBindingResult().getFieldError())
.map(DefaultMessageSourceResolvable::getDefaultMessage)
.orElse("요청 형식이 잘못되었습니다.");
return ResponseEntity.badRequest()
.body(new ExceptionResponse(HttpStatus.BAD_REQUEST.toString(), message));
}

@ExceptionHandler(ConstraintViolationException.class)
public ResponseEntity<ExceptionResponse> handleConstraintViolationException(ConstraintViolationException e) {
return ResponseEntity.badRequest()
.body(new ExceptionResponse(HttpStatus.BAD_REQUEST.toString(), e.getMessage()));
}

@ExceptionHandler(StaccatoException.class)
public ResponseEntity<ExceptionResponse> handleStaccatoException(StaccatoException e) {
return ResponseEntity.badRequest()
.body(new ExceptionResponse(HttpStatus.BAD_REQUEST.toString(), e.getMessage()));
linirini marked this conversation as resolved.
Show resolved Hide resolved
}

@ExceptionHandler(RuntimeException.class)
public ResponseEntity<ExceptionResponse> handleInternalServerErrorException() {
return ResponseEntity.internalServerError()
.body(new ExceptionResponse(HttpStatus.INTERNAL_SERVER_ERROR.toString(), "예기치 못한 서버 오류입니다. 다시 시도해주세요."));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.staccato.exception;

public class StaccatoException extends RuntimeException {
public StaccatoException() {
super();
}

public StaccatoException(String message) {
super(message);
}

public StaccatoException(String message, Throwable cause) {
super(message, cause);
}

public StaccatoException(Throwable cause) {
super(cause);
}
}
14 changes: 14 additions & 0 deletions backend/src/main/java/com/staccato/member/domain/Member.java
Original file line number Diff line number Diff line change
@@ -1,19 +1,27 @@
package com.staccato.member.domain;

import java.util.List;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.OneToMany;

import org.hibernate.annotations.SQLDelete;

import com.staccato.config.domain.BaseEntity;
import com.staccato.travel.domain.TravelMember;

import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.NonNull;

@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@SQLDelete(sql = "UPDATE member SET is_deleted = true WHERE id = ?")
public class Member extends BaseEntity {
Expand All @@ -24,4 +32,10 @@ public class Member extends BaseEntity {
private String nickname;
@Column(columnDefinition = "TEXT")
private String imageUrl;

@Builder
public Member(@NonNull String nickname, String imageUrl) {
this.nickname = nickname;
this.imageUrl = imageUrl;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.staccato.member.repository;

import org.springframework.data.jpa.repository.JpaRepository;

import com.staccato.member.domain.Member;

public interface MemberRepository extends JpaRepository<Member, Long> {
}
8 changes: 8 additions & 0 deletions backend/src/main/java/com/staccato/pin/domain/Pin.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@
import com.staccato.config.domain.BaseEntity;

import lombok.AccessLevel;
import lombok.Builder;
import lombok.NoArgsConstructor;
import lombok.NonNull;

@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
Expand All @@ -24,4 +26,10 @@ public class Pin extends BaseEntity {
private String place;
@Column(nullable = false)
private String address;

@Builder
public Pin(@NonNull String place, @NonNull String address) {
this.place = place;
this.address = address;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.staccato.pin.repository;

import org.springframework.data.jpa.repository.JpaRepository;

import com.staccato.pin.domain.Pin;

public interface PinRepository extends JpaRepository<Pin, Long> {
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,19 @@
package com.staccato.travel.controller;

import java.net.URI;

import jakarta.validation.Valid;

import org.springframework.http.ResponseEntity;
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;

import com.staccato.config.auth.MemberId;
import com.staccato.travel.service.TravelService;
import com.staccato.travel.service.dto.request.TravelRequest;
import com.staccato.travel.service.dto.response.TravelResponse;

import lombok.RequiredArgsConstructor;

Expand All @@ -12,4 +22,10 @@
@RequiredArgsConstructor
public class TravelController {
private final TravelService travelService;

@PostMapping
public ResponseEntity<Void> createTravel(@Valid @RequestBody TravelRequest travelRequest, @MemberId Long memberId) {
TravelResponse travelResponse = travelService.createTravel(travelRequest, memberId);
return ResponseEntity.created(URI.create("/travels/" + travelResponse.travelId())).build();
}
}
Loading