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

[BE/FIX] git hook chmod 권한이 없는 에러를 해결한다. #299

Merged
merged 14 commits into from
Mar 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
28 changes: 28 additions & 0 deletions BE/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
## TechStack

| 카테고리 | 기술 |
| --- | --- |
| Language, Framework | Java, Spring Boot |
| Database | MySQL, Redis, Flyway |
| ORM | Spring Data JPA, QueryDSL |
| 운영 환경 구축 | ECR, ECS, EC2, Docker |
| Build | Gradle |
| CI/CD | Github Actions |
| library | Spring Open Feign, OAuth 2.0 |

## docs
[서브 도메인 적용하기](https://velog.io/@kssumin/%EC%84%9C%EB%B8%8C-%EB%8F%84%EB%A9%94%EC%9D%B8-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0)

[타임존 설정](https://velog.io/@kssumin/DB%EC%97%90-%EB%93%A4%EC%96%B4%EA%B0%80%EB%8A%94-%EC%8B%9C%EA%B0%84%EC%9D%B4-%EC%9D%B4%EC%83%81%ED%95%98%EB%8B%A4)

[분기 처리 코드 리팩터링하기](https://velog.io/@kssumin/%EB%B6%84%EA%B8%B0%EC%B2%98%EB%A6%AC-%EC%BD%94%EB%93%9C-%EB%A6%AC%ED%8C%A9%ED%84%B0%EB%A7%81-%ED%95%98%EA%B8%B0)

[슬랙 API 호출 코드를 추상화하기](https://velog.io/@kssumin/%EC%8A%AC%EB%9E%99-API-%ED%98%B8%EC%B6%9C-%EC%BD%94%EB%93%9C%EB%A5%BC-%EC%B6%94%EC%83%81%ED%99%94%ED%95%98%EA%B8%B0)

[200 status code만 사용하지 말자](https://velog.io/@kssumin/EEOS-%EC%BA%90%EC%8B%B1)

## 실행방법
1. git clone
2. 환경변수를 설정해줍니다.(env.properties)
3. docker-compose를 통해 db를 실행합니다.
4. spring boot를 실행합니다.
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@Service
Expand All @@ -17,6 +18,7 @@ public class QueryTeamBuildingTargetService {
private final TeamBuildingTargetRepository teamBuildingTargetRepository;
private final TeamBuildingTargetEntityConverter entityConverter;

@Transactional(readOnly = true, propagation = Propagation.REQUIRES_NEW)
public TeamBuildingTargetModel getTarget(Long memberId, Long teamBuildingId) {
return teamBuildingTargetRepository
.findByTeamBuildingIdAndMemberId(teamBuildingId, memberId)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,6 @@ Optional<TeamBuildingTargetEntity> findByTeamBuildingIdAndMemberId(
Long teamBuildingId, Long memberId);

List<TeamBuildingTargetEntity> findByTeamBuildingId(Long teamBuildingId);

void deleteByTeamBuildingId(Long teamBuildingId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.blackcompany.eeos.teamBuilding.application.event;

import lombok.Getter;

@Getter
public class DeletedTeamBuildingEvent {
private final Long teamBuildingId;

private DeletedTeamBuildingEvent(Long teamBuildingId) {
this.teamBuildingId = teamBuildingId;
}

/**
* 팀빌딩 삭제 이벤트를 생성한다.
*
* <p>이벤트 생성에서 예외가 발생하면 서비스 로직에 영향을 주므로 이벤트 리스너에서 핸들링을 할 때 예외 처리를 한다.
*
* @param teamBuildingId 삭제된 팀빌딩 정보
* @return
*/
public static DeletedTeamBuildingEvent of(Long teamBuildingId) {
return new DeletedTeamBuildingEvent(teamBuildingId);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.blackcompany.eeos.teamBuilding.application.event;

import com.blackcompany.eeos.target.persistence.TeamBuildingTargetRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.event.TransactionPhase;
import org.springframework.transaction.event.TransactionalEventListener;
import org.springframework.transaction.support.TransactionSynchronizationManager;

@Component
@RequiredArgsConstructor
@Slf4j
public class DeletedTeamBuildingEventListener {
private final TeamBuildingTargetRepository teamBuildingTargetRepository;

@Async
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void handleDeletedTeamBuilding(DeletedTeamBuildingEvent event) {
log.info(
"팀빌딩 삭제 Transaction committed: {}",
TransactionSynchronizationManager.isActualTransactionActive());
teamBuildingTargetRepository.deleteByTeamBuildingId(event.getTeamBuildingId());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import com.blackcompany.eeos.teamBuilding.application.dto.EachMemberResponse;
import com.blackcompany.eeos.teamBuilding.application.dto.ResultTeamBuildingResponse;
import com.blackcompany.eeos.teamBuilding.application.dto.converter.ResultTeamBuildingResponseConverter;
import com.blackcompany.eeos.teamBuilding.application.event.DeletedTeamBuildingEvent;
import com.blackcompany.eeos.teamBuilding.application.exception.CompleteTeamBuildingException;
import com.blackcompany.eeos.teamBuilding.application.exception.EndTeamBuildingException;
import com.blackcompany.eeos.teamBuilding.application.exception.NotFoundProgressTeamBuildingException;
Expand All @@ -20,6 +21,7 @@
import com.blackcompany.eeos.teamBuilding.application.model.converter.TeamBuildingResultConverter;
import com.blackcompany.eeos.teamBuilding.application.usecase.CompleteTeamBuildingUsecase;
import com.blackcompany.eeos.teamBuilding.application.usecase.CreateTeamBuildingUsecase;
import com.blackcompany.eeos.teamBuilding.application.usecase.DeleteTeamBuildingUsecase;
import com.blackcompany.eeos.teamBuilding.application.usecase.EndTeamBuildingUsecase;
import com.blackcompany.eeos.teamBuilding.application.usecase.GetResultTeamBuildingUsecase;
import com.blackcompany.eeos.teamBuilding.application.usecase.GetTeamBuildingUsecase;
Expand All @@ -35,6 +37,7 @@
import java.util.function.Supplier;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

Expand All @@ -46,7 +49,8 @@ public class TeamBuildingService
EndTeamBuildingUsecase,
CompleteTeamBuildingUsecase,
GetResultTeamBuildingUsecase,
GetTeamBuildingUsecase {
GetTeamBuildingUsecase,
DeleteTeamBuildingUsecase {
private final RestrictTeamBuildingService restrictTeamBuildingService;
private final TeamBuildingRequestConverter requestConverter;
private final TeamBuildingEntityConverter entityConverter;
Expand All @@ -60,6 +64,7 @@ public class TeamBuildingService
private final TeamBuildingResultConverter teamBuildingResultConverter;
private final MemberRepository memberRepository;
private final ResultTeamBuildingResponseConverter responseConverter;
private final ApplicationEventPublisher publisher;

@Override
@Transactional
Expand All @@ -74,7 +79,7 @@ public void create(Long memberId, CreateTeamBuildingRequest request) {

@Override
@Transactional
public void delete(Long memberId) {
public void end(Long memberId) {
TeamBuildingStatus status = TeamBuildingStatus.COMPLETE;

TeamBuildingModel model =
Expand Down Expand Up @@ -135,6 +140,24 @@ public QueryTeamBuildingResponse getTeamBuilding(Long memberId) {
.build();
}

@Override
@Transactional
public void delete(Long memberId) {
TeamBuildingStatus status = TeamBuildingStatus.PROGRESS;

TeamBuildingModel model =
teamBuildingRepository
.findByStatus(status)
.map(entityConverter::from)
.orElseThrow(() -> new NotFoundTeamBuildingStatusException(status.getStatus()));

model.validateEdit(memberId);

teamBuildingRepository.delete(entityConverter.toEntity(model));
restrictTeamBuildingService.subtractTeamBuilding();
publisher.publishEvent(DeletedTeamBuildingEvent.of(model.getId()));
}

private List<List<EachMemberResponse>> combines(
List<List<MemberEntity>> members, List<List<Long>> memberIds) {
return members.stream()
Expand Down Expand Up @@ -191,8 +214,7 @@ private void validateReadAccessibility(Long memberId, Long programId) {
generateNotFoundProgressTeamBuildingException() {
if (teamBuildingRepository.existsByStatus(TeamBuildingStatus.COMPLETE)) {
return CompleteTeamBuildingException::new;
} else {
return EndTeamBuildingException::new;
}
return EndTeamBuildingException::new;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.blackcompany.eeos.teamBuilding.application.usecase;

public interface DeleteTeamBuildingUsecase {
/**
* 팀빌딩 완료를 요청한다.
*
* @param memberId 팀빌딩 작성자
*/
void delete(Long memberId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ public interface EndTeamBuildingUsecase {
*
* @param memberId 팀빌딩 작성자
*/
void delete(Long memberId);
void end(Long memberId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import com.blackcompany.eeos.teamBuilding.application.dto.ValidateTeamBuildingResponse;
import com.blackcompany.eeos.teamBuilding.application.usecase.CompleteTeamBuildingUsecase;
import com.blackcompany.eeos.teamBuilding.application.usecase.CreateTeamBuildingUsecase;
import com.blackcompany.eeos.teamBuilding.application.usecase.DeleteTeamBuildingUsecase;
import com.blackcompany.eeos.teamBuilding.application.usecase.EndTeamBuildingUsecase;
import com.blackcompany.eeos.teamBuilding.application.usecase.GetResultTeamBuildingUsecase;
import com.blackcompany.eeos.teamBuilding.application.usecase.GetTeamBuildingUsecase;
Expand All @@ -36,6 +37,7 @@ public class TeamBuildingController {
private final CompleteTeamBuildingUsecase completeTeamBuildingUsecase;
private final GetResultTeamBuildingUsecase getResultTeamBuildingUsecase;
private final GetTeamBuildingUsecase getTeamBuildingUsecase;
private final DeleteTeamBuildingUsecase deleteTeamBuildingUsecase;

@PostMapping
public ApiResponse<SuccessBody<Void>> create(
Expand All @@ -46,7 +48,7 @@ public ApiResponse<SuccessBody<Void>> create(

@DeleteMapping("/end")
public ApiResponse<SuccessBody<Void>> end(@Member Long memberId) {
endTeamBuildingUsecase.delete(memberId);
endTeamBuildingUsecase.end(memberId);
return ApiResponseGenerator.success(HttpStatus.OK, MessageCode.DELETE);
}

Expand Down Expand Up @@ -75,4 +77,10 @@ public ApiResponse<SuccessBody<QueryTeamBuildingResponse>> getTeamBuilding(
QueryTeamBuildingResponse response = getTeamBuildingUsecase.getTeamBuilding(memberId);
return ApiResponseGenerator.success(response, HttpStatus.OK, MessageCode.GET);
}

@DeleteMapping
public ApiResponse<SuccessBody<Void>> deleteTeamBuilding(@Member Long memberId) {
deleteTeamBuildingUsecase.delete(memberId);
return ApiResponseGenerator.success(HttpStatus.OK, MessageCode.DELETE);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@ void save_team_building() {
@DisplayName("진행 중인 팀빌딩의 작성자가 아니라면 수정 권한이 없음 예외가 발생한다.")
void denied_edit_team_building() {
// given
TeamBuildingEntity 생성된_팀빌딩 = TeamBuildingFixture.팀빌딩(TeamBuildingStatus.COMPLETE, 1L);
when(teamBuildingRepository.findByStatus(TeamBuildingStatus.COMPLETE))
TeamBuildingEntity 생성된_팀빌딩 = TeamBuildingFixture.팀빌딩(TeamBuildingStatus.PROGRESS, 1L);
when(teamBuildingRepository.findByStatus(TeamBuildingStatus.PROGRESS))
.thenReturn(Optional.of(생성된_팀빌딩));

// when & then
Expand Down
6 changes: 4 additions & 2 deletions BE/eeos/tasks/install-git-hooks.gradle
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
tasks.create(name: 'gitExecutableHooks') {
Runtime.getRuntime().exec("chmod -R +x ../../.git/hooks/");
doLast {
Runtime.getRuntime().exec("chmod -R +x ../../.git/hooks/")
}
}

task installGitHooks(type: Copy) {
String scriptDir = rootProject.rootDir.toString() + '/scripts'
from new File(scriptDir, 'pre-commit')
into { new File(rootProject.rootDir, '../../.git/hooks') }
into { new File(rootProject.rootDir, '.git/hooks') }
}

gitExecutableHooks.dependsOn installGitHooks
Expand Down
Loading