-
Notifications
You must be signed in to change notification settings - Fork 1
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
[DEV-56] Vod Processing Job 생성 API 구현 #195
Changes from 15 commits
fcc2447
1654763
c812cb6
1888173
4b54843
c0223be
9d8a634
13489ce
1a5fdc1
6bbfcce
3a4f0ac
664558f
04b1e0c
27df89c
8474a0b
dee57aa
c2b9e04
27ac98b
6bef834
a6cd14d
b07a0cf
d966e20
507a8f5
e89aee7
e32445b
02c6ea1
59865e5
7595b8f
21fe8e5
c874a72
227efa9
01c039d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package ddingdong.ddingdongBE.domain.vodprocessing.controller; | ||
|
||
import ddingdong.ddingdongBE.domain.vodprocessing.controller.dto.request.CreatePendingVodProcessingJobRequest; | ||
import ddingdong.ddingdongBE.domain.vodprocessing.service.FacadeVodProcessingJobService; | ||
import io.swagger.v3.oas.annotations.Hidden; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.web.bind.annotation.PostMapping; | ||
import org.springframework.web.bind.annotation.RestController; | ||
|
||
@Hidden | ||
@RestController("/server/internal/trigger/vod-processing-job") | ||
5uhwann marked this conversation as resolved.
Show resolved
Hide resolved
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. url 매핑은 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 해당 컨트롤러는 클라이언트에게 제공되는 API가 아니라 trigging을 위한 API 여서 따로 swagger 명세를 안하기 위해서 인터페이스를 명세하지 않았습니다. |
||
@RequiredArgsConstructor | ||
public class VodProcessingJobController { | ||
|
||
private final FacadeVodProcessingJobService facadeVodProcessingJobService; | ||
|
||
@PostMapping() | ||
public void createPending(CreatePendingVodProcessingJobRequest request) { | ||
facadeVodProcessingJobService.create(request.toCommand()); | ||
} | ||
5uhwann marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package ddingdong.ddingdongBE.domain.vodprocessing.controller.dto.request; | ||
|
||
import ddingdong.ddingdongBE.domain.vodprocessing.service.dto.command.CreatePendingVodProcessingJobCommand; | ||
|
||
public record CreatePendingVodProcessingJobRequest( | ||
String convertJobId, | ||
String userId | ||
) { | ||
Comment on lines
+5
to
+8
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 요청 DTO에 유효성 검증이 필요합니다. 필드 유효성 검증을 위한 Jakarta Validation 어노테이션 추가가 필요합니다: public record CreatePendingVodProcessingJobRequest(
@NotBlank(message = "변환 작업 ID는 필수입니다")
String convertJobId,
@NotBlank(message = "사용자 ID는 필수입니다")
String userId
) { |
||
|
||
public CreatePendingVodProcessingJobCommand toCommand() { | ||
return new CreatePendingVodProcessingJobCommand(convertJobId, userId); | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
package ddingdong.ddingdongBE.domain.vodprocessing.entity; | ||
|
||
public enum ConvertJobStatus { | ||
PENDING | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
package ddingdong.ddingdongBE.domain.vodprocessing.entity; | ||
|
||
public enum VodNotificationStatus { | ||
} |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,50 @@ | ||||||||||||||||||||||||||||||||||||||||||
package ddingdong.ddingdongBE.domain.vodprocessing.entity; | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
import ddingdong.ddingdongBE.common.BaseEntity; | ||||||||||||||||||||||||||||||||||||||||||
import jakarta.persistence.Column; | ||||||||||||||||||||||||||||||||||||||||||
import jakarta.persistence.Entity; | ||||||||||||||||||||||||||||||||||||||||||
import jakarta.persistence.EnumType; | ||||||||||||||||||||||||||||||||||||||||||
import jakarta.persistence.Enumerated; | ||||||||||||||||||||||||||||||||||||||||||
import jakarta.persistence.FetchType; | ||||||||||||||||||||||||||||||||||||||||||
import jakarta.persistence.GeneratedValue; | ||||||||||||||||||||||||||||||||||||||||||
import jakarta.persistence.GenerationType; | ||||||||||||||||||||||||||||||||||||||||||
import jakarta.persistence.Id; | ||||||||||||||||||||||||||||||||||||||||||
import jakarta.persistence.JoinColumn; | ||||||||||||||||||||||||||||||||||||||||||
import jakarta.persistence.OneToOne; | ||||||||||||||||||||||||||||||||||||||||||
import lombok.AccessLevel; | ||||||||||||||||||||||||||||||||||||||||||
import lombok.Builder; | ||||||||||||||||||||||||||||||||||||||||||
import lombok.Getter; | ||||||||||||||||||||||||||||||||||||||||||
import lombok.NoArgsConstructor; | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
@Entity | ||||||||||||||||||||||||||||||||||||||||||
@Getter | ||||||||||||||||||||||||||||||||||||||||||
@NoArgsConstructor(access = AccessLevel.PROTECTED) | ||||||||||||||||||||||||||||||||||||||||||
public class VodProcessingJob extends BaseEntity { | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
@Id | ||||||||||||||||||||||||||||||||||||||||||
@GeneratedValue(strategy = GenerationType.IDENTITY) | ||||||||||||||||||||||||||||||||||||||||||
private Long id; | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
@OneToOne(fetch = FetchType.LAZY) | ||||||||||||||||||||||||||||||||||||||||||
@JoinColumn(name = "notification_id") | ||||||||||||||||||||||||||||||||||||||||||
private VodProcessingNotification vodProcessingNotification; | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
@Column(nullable = false) | ||||||||||||||||||||||||||||||||||||||||||
private String convertJobId; | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
@Column(nullable = false) | ||||||||||||||||||||||||||||||||||||||||||
private String userId; | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
@Enumerated(EnumType.STRING) | ||||||||||||||||||||||||||||||||||||||||||
private ConvertJobStatus convertJobStatus; | ||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+32
to
+39
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion 상태 값 제약조건 추가 필요
@Column(nullable = false)
+@Size(max = 255)
private String convertJobId;
@Column(nullable = false)
+@Size(max = 255)
private String userId;
@Enumerated(EnumType.STRING)
+@Column(nullable = false)
+@Builder.Default
private ConvertJobStatus convertJobStatus = ConvertJobStatus.PENDING; 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
@Builder | ||||||||||||||||||||||||||||||||||||||||||
private VodProcessingJob(Long id, VodProcessingNotification vodProcessingNotification, String convertJobId, | ||||||||||||||||||||||||||||||||||||||||||
String userId, ConvertJobStatus convertJobStatus) { | ||||||||||||||||||||||||||||||||||||||||||
this.id = id; | ||||||||||||||||||||||||||||||||||||||||||
this.vodProcessingNotification = vodProcessingNotification; | ||||||||||||||||||||||||||||||||||||||||||
this.convertJobId = convertJobId; | ||||||||||||||||||||||||||||||||||||||||||
this.userId = userId; | ||||||||||||||||||||||||||||||||||||||||||
this.convertJobStatus = convertJobStatus; | ||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
package ddingdong.ddingdongBE.domain.vodprocessing.entity; | ||
|
||
import ddingdong.ddingdongBE.common.BaseEntity; | ||
import jakarta.persistence.Column; | ||
import jakarta.persistence.Entity; | ||
import jakarta.persistence.EnumType; | ||
import jakarta.persistence.Enumerated; | ||
import jakarta.persistence.GeneratedValue; | ||
import jakarta.persistence.GenerationType; | ||
import jakarta.persistence.Id; | ||
import java.time.LocalDateTime; | ||
import lombok.AccessLevel; | ||
import lombok.Getter; | ||
import lombok.NoArgsConstructor; | ||
|
||
@Entity | ||
@Getter | ||
@NoArgsConstructor(access = AccessLevel.PROTECTED) | ||
public class VodProcessingNotification extends BaseEntity { | ||
|
||
@Id | ||
@GeneratedValue(strategy = GenerationType.IDENTITY) | ||
private Long id; | ||
|
||
private LocalDateTime expiredAt; | ||
|
||
private LocalDateTime sentAt; | ||
|
||
@Column(nullable = false) | ||
private int retryCount; | ||
5uhwann marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
@Enumerated(EnumType.STRING) | ||
@Column(name = "notification_status", nullable = false) | ||
private VodNotificationStatus vodNotificationStatus; | ||
|
||
} | ||
5uhwann marked this conversation as resolved.
Show resolved
Hide resolved
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package ddingdong.ddingdongBE.domain.vodprocessing.repository; | ||
|
||
import ddingdong.ddingdongBE.domain.vodprocessing.entity.VodProcessingJob; | ||
import java.util.Optional; | ||
import org.springframework.data.jpa.repository.JpaRepository; | ||
import org.springframework.stereotype.Repository; | ||
|
||
@Repository | ||
public interface VodProcessingJobRepository extends JpaRepository<VodProcessingJob, Long> { | ||
|
||
Optional<VodProcessingJob> findByConvertJobId(String convertJobId); | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package ddingdong.ddingdongBE.domain.vodprocessing.service; | ||
|
||
import ddingdong.ddingdongBE.domain.vodprocessing.service.dto.command.CreatePendingVodProcessingJobCommand; | ||
|
||
public interface FacadeVodProcessingJobService { | ||
|
||
Long create(CreatePendingVodProcessingJobCommand command); | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
package ddingdong.ddingdongBE.domain.vodprocessing.service; | ||
|
||
import ddingdong.ddingdongBE.domain.vodprocessing.service.dto.command.CreatePendingVodProcessingJobCommand; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.stereotype.Service; | ||
import org.springframework.transaction.annotation.Transactional; | ||
|
||
@Service | ||
@Transactional(readOnly = true) | ||
5uhwann marked this conversation as resolved.
Show resolved
Hide resolved
|
||
@RequiredArgsConstructor | ||
public class FacadeVodProcessingJobServiceImpl implements FacadeVodProcessingJobService { | ||
|
||
private final VodProcessingJobService vodProcessingJobService; | ||
|
||
|
||
@Override | ||
public Long create(CreatePendingVodProcessingJobCommand command) { | ||
return vodProcessingJobService.save(command.toPendingVodProcessingJob()); | ||
} | ||
5uhwann marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
package ddingdong.ddingdongBE.domain.vodprocessing.service; | ||
|
||
import ddingdong.ddingdongBE.common.exception.PersistenceException.ResourceNotFound; | ||
import ddingdong.ddingdongBE.domain.vodprocessing.entity.VodProcessingJob; | ||
import ddingdong.ddingdongBE.domain.vodprocessing.repository.VodProcessingJobRepository; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.stereotype.Service; | ||
import org.springframework.transaction.annotation.Transactional; | ||
|
||
@Service | ||
@Transactional(readOnly = true) | ||
@RequiredArgsConstructor | ||
public class GeneralVodProcessingJobService implements VodProcessingJobService { | ||
|
||
private final VodProcessingJobRepository vodProcessingJobRepository; | ||
|
||
@Override | ||
@Transactional | ||
public Long save(VodProcessingJob vodProcessingJob) { | ||
VodProcessingJob saveVodProcessingJob = vodProcessingJobRepository.save(vodProcessingJob); | ||
return saveVodProcessingJob.getId(); | ||
} | ||
5uhwann marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
@Override | ||
public VodProcessingJob getById(Long vodProcessingJobId) { | ||
return vodProcessingJobRepository.findById(vodProcessingJobId) | ||
.orElseThrow(() -> new ResourceNotFound( | ||
"VodProcessingJob(vodProcessingJobId=" + vodProcessingJobId + ")를 찾을 수 없습니다.")); | ||
} | ||
|
||
@Override | ||
public VodProcessingJob getByConvertJobId(String convertJobId) { | ||
return vodProcessingJobRepository.findByConvertJobId(convertJobId) | ||
.orElseThrow(() -> new ResourceNotFound( | ||
"VodProcessingJob(convertJobId=" + convertJobId + ")를 찾을 수 없습니다.")); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package ddingdong.ddingdongBE.domain.vodprocessing.service; | ||
|
||
import ddingdong.ddingdongBE.domain.vodprocessing.entity.VodProcessingJob; | ||
|
||
public interface VodProcessingJobService { | ||
|
||
Long save(VodProcessingJob vodProcessingJob); | ||
|
||
VodProcessingJob getById(Long vodProcessingJobId); | ||
|
||
VodProcessingJob getByConvertJobId(String convertJobId); | ||
|
||
5uhwann marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
package ddingdong.ddingdongBE.domain.vodprocessing.service.dto.command; | ||
|
||
import ddingdong.ddingdongBE.domain.vodprocessing.entity.ConvertJobStatus; | ||
import ddingdong.ddingdongBE.domain.vodprocessing.entity.VodProcessingJob; | ||
|
||
public record CreatePendingVodProcessingJobCommand( | ||
String convertJobId, | ||
String userId | ||
) { | ||
|
||
public VodProcessingJob toPendingVodProcessingJob() { | ||
return VodProcessingJob.builder() | ||
.convertJobId(convertJobId) | ||
.userId(userId) | ||
.convertJobStatus(ConvertJobStatus.PENDING) | ||
.build(); | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -24,7 +24,7 @@ public UploadUrlResponse getPreSignedUrl(PrincipalDetails principalDetails, Stri | |||||||||||||||||||||||
LocalDateTime now = LocalDateTime.now(); | ||||||||||||||||||||||||
GeneratePreSignedUrlRequestQuery query = | ||||||||||||||||||||||||
s3FileService.generatePresignedUrlRequest( | ||||||||||||||||||||||||
new GeneratePreSignedUrlRequestCommand(now, user.getAuthId(), fileName)); | ||||||||||||||||||||||||
new GeneratePreSignedUrlRequestCommand(now, user.getId(), fileName)); | ||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion 사용자 유효성 검사 추가 필요
다음과 같은 방식으로 개선을 제안드립니다: @Override
public UploadUrlResponse getPreSignedUrl(PrincipalDetails principalDetails, String fileName) {
User user = principalDetails.getUser();
+ if (user == null || user.getId() == null) {
+ throw new IllegalArgumentException("유효하지 않은 사용자입니다.");
+ }
LocalDateTime now = LocalDateTime.now();
GeneratePreSignedUrlRequestQuery query =
s3FileService.generatePresignedUrlRequest(
new GeneratePreSignedUrlRequestCommand(now, user.getId(), fileName)); 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||
URL presingedUrl = s3FileService.getPresignedUrl(query.generatePresignedUrlRequest()); | ||||||||||||||||||||||||
return UploadUrlResponse.of(query, presingedUrl); | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
내부 API 엔드포인트에 대한 보안 강화 필요
내부 API 엔드포인트(
/internal/**
)에 대한 무제한 접근 허용은 잠재적인 보안 위험을 초래할 수 있습니다. 내부 API는 일반적으로 더 엄격한 접근 제어가 필요합니다.다음과 같은 보안 강화 방안을 고려해 주세요:
예시 구현:
또는 커스텀 필터를 통한 API 키 검증:
💡 Codebase verification
내부 API 엔드포인트 보안 검증 완료
내부 API 엔드포인트의 사용이 적절하게 제한되어 있습니다:
/server/internal/trigger/vod-processing-job
엔드포인트만 내부 API로 사용되고 있으며,@Hidden
어노테이션으로 문서화에서 제외되어 있습니다./internal/**
경로에 대한 접근 제어가permitAll()
로 설정되어 있어, 보안 강화가 필요할 수 있습니다.🔗 Analysis chain
내부 API 엔드포인트 사용 현황 확인 필요
코드베이스 전체에서 내부 API 엔드포인트의 사용 패턴을 확인하여 일관된 보안 정책을 적용해야 합니다.
Let me gather more information about the internal endpoints and their security configuration.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
Length of output: 1221
Script:
Length of output: 1610