Skip to content
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
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ public RsData<UploadUrlGetResponse> getUploadUrl(@RequestParam String filename)

@GetMapping("/videos/download")
@Operation(summary = "๋‹ค์šด๋กœ๋“œ์šฉ URL ์š”์ฒญ", description = "ํŒŒ์ผ ๋‹ค์šด๋กœ๋“œ๋ฅผ ์œ„ํ•œ Presigned URL์„ ๋ฐœ๊ธ‰๋ฐ›์Šต๋‹ˆ๋‹ค.")
public RsData<DownLoadUrlGetResponse> getDownloadUrls(@RequestParam String objectKey) {
PresignedUrlResponse downloadUrl = fileManager.getDownloadUrl(objectKey);
public RsData<DownLoadUrlGetResponse> getDownloadUrls(@RequestParam String uuid, @RequestParam String resolution) {
PresignedUrlResponse downloadUrl = fileManager.getDownloadUrl(uuid, resolution);
DownLoadUrlGetResponse response = new DownLoadUrlGetResponse(downloadUrl.url().toString(), downloadUrl.expiresAt());
return new RsData<>("200", "๋‹ค์šด๋กœ๋“œ์šฉ URL ์š”์ฒญ์™„๋ฃŒ", response);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@
@Repository
public interface VideoRepository extends JpaRepository<Video, Integer> {
Optional<Video> findByUuid(String uuid);
boolean existsByUuid(String uuid);
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@

import java.net.URL;
import java.time.LocalDateTime;
import java.util.Set;
import java.util.UUID;

@Service
@RequiredArgsConstructor
public class FileManager {
private final VideoService videoService;
private final S3Service s3Service;
private final Set<String> validResolutions = Set.of("480p", "720p", "1080p");

public PresignedUrlResponse getUploadUrl(String filename) {
String contentType = validateAndGetContentType(filename);
Expand Down Expand Up @@ -53,8 +55,11 @@ private String validateAndGetContentType(String filename) {
}
}

public PresignedUrlResponse getDownloadUrl(String objectKey) {
public PresignedUrlResponse getDownloadUrl(String uuid, String resolution) {
Integer expires = 60;
validateResolution(resolution);
videoService.isExistByUuid(uuid);
String objectKey = "transcoded/videos/" + uuid + "/" + resolution + "/manifest.mpd";
URL url = s3Service.generateDownloadUrl(objectKey, expires);
LocalDateTime expiresAt = LocalDateTime.now().plusMinutes(expires);
return new PresignedUrlResponse(url, expiresAt);
Expand All @@ -68,4 +73,10 @@ public void updateVideoStatus(String videoId, String status) {
videoService.createVideo(videoId, status, "/", 0);
}
}

public void validateResolution(String resolution) {
if (!validResolutions.contains(resolution)) {
throw new ServiceException("400", "์ง€์›ํ•˜์ง€ ์•Š๋Š” ํ•ด์ƒ๋„์ž…๋‹ˆ๋‹ค: " + resolution);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,10 @@ public Video updateStatus(String uuid, String status) {
news.updateStatus(status);
return videoRepository.save(news);
}

public void isExistByUuid(String uuid) {
if (!videoRepository.existsByUuid(uuid)) {
throw new ServiceException("404", "Video not found");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
.requestMatchers("/auth/**").permitAll()
.requestMatchers("/actuator/**").permitAll()
.requestMatchers("/swagger-ui/**", "/v3/api-docs/**", "/swagger-resources/**").permitAll()
.requestMatchers("/videos/upload").permitAll()
.requestMatchers("/ws-chat/**").permitAll()
.anyRequest().authenticated()
)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package com.back.domain.file.video.controller;

import com.back.domain.file.video.dto.service.PresignedUrlResponse;
import com.back.domain.file.video.service.FileManager;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import org.springframework.test.web.servlet.MockMvc;

import java.net.URL;
import java.time.LocalDateTime;

import static org.mockito.ArgumentMatchers.anyString;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@SpringBootTest
@AutoConfigureMockMvc
class VideoControllerTest {

@Autowired
private MockMvc mockMvc;

@MockitoBean
private FileManager fileManager;

@Test
@DisplayName("์—…๋กœ๋“œ URL ์š”์ฒญ - ์„ฑ๊ณต")
@WithMockUser(roles = "ADMIN")
void testGetUploadUrl() throws Exception {
URL mockUrl = new URL("https://bucket.s3.amazonaws.com/123e4567-e89b-12d3-a456-426614174000");
LocalDateTime expiresAt = LocalDateTime.now().plusMinutes(10);

Mockito.when(fileManager.getUploadUrl(anyString()))
.thenReturn(new PresignedUrlResponse(mockUrl, expiresAt));

mockMvc.perform(get("/videos/upload")
.param("filename", "test.mp4")
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$.resultCode").value("200"))
.andExpect(jsonPath("$.msg").value("์—…๋กœ๋“œ์šฉ URL ์š”์ฒญ์™„๋ฃŒ"))
.andExpect(jsonPath("$.data.url").value(mockUrl.toString()))
.andExpect(jsonPath("$.data.uuid").value("123e4567-e89b-12d3-a456-426614174000"));
}

@Test
@DisplayName("์—…๋กœ๋“œ URL ์š”์ฒญ - ๊ถŒํ•œ ๋ถ€์กฑ")
@WithMockUser(roles = "USER")
// ADMIN ์•„๋‹˜
void testGetUploadUrl_Unauthorized() throws Exception {
mockMvc.perform(get("/videos/upload")
.param("filename", "test.mp4")
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isForbidden()); // 403 ๊ถŒํ•œ ๋ถ€์กฑ
}

@Test
@DisplayName("์—…๋กœ๋“œ URL ์š”์ฒญ - ํŒŒ์ผ๋ช…์— ํ™•์žฅ์ž๊ฐ€ ์—†๋Š” ๊ฒฝ์šฐ")
@WithMockUser(roles = "ADMIN")
void testGetUploadUrl_NoExtension() throws Exception {
Mockito.when(fileManager.getUploadUrl(anyString()))
.thenThrow(new com.back.global.exception.ServiceException("400", "์ง€์›ํ•˜์ง€ ์•Š๋Š” ๋™์˜์ƒ ํŒŒ์ผ ํ˜•์‹์ž…๋‹ˆ๋‹ค: testfile"));

mockMvc.perform(get("/videos/upload")
.param("filename", "testfile")
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isBadRequest())
.andExpect(jsonPath("$.resultCode").value("400"))
.andExpect(jsonPath("$.msg").value("์ง€์›ํ•˜์ง€ ์•Š๋Š” ๋™์˜์ƒ ํŒŒ์ผ ํ˜•์‹์ž…๋‹ˆ๋‹ค: testfile"));
}

@Test
@DisplayName("๋‹ค์šด๋กœ๋“œ URL ์š”์ฒญ - ์„ฑ๊ณต")
@WithMockUser
void testGetDownloadUrls() throws Exception {
URL mockUrl = new URL("https://bucket.s3.amazonaws.com/test.mp4");
LocalDateTime expiresAt = LocalDateTime.now().plusMinutes(10);

Mockito.when(fileManager.getDownloadUrl(anyString(), anyString()))
.thenReturn(new PresignedUrlResponse(mockUrl, expiresAt));

mockMvc.perform(get("/videos/download")
.param("uuid", "123e4567-e89b-12d3-a456-426614174000")
.param("resolution", "1080p")
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$.resultCode").value("200"))
.andExpect(jsonPath("$.msg").value("๋‹ค์šด๋กœ๋“œ์šฉ URL ์š”์ฒญ์™„๋ฃŒ"))
.andExpect(jsonPath("$.data.url").value(mockUrl.toString()));
}

}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.back.domain.file.entity;
package com.back.domain.file.video.entity;

import com.back.domain.file.video.entity.Video;
import org.junit.jupiter.api.DisplayName;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.back.domain.file.service;
package com.back.domain.file.video.service;

import com.back.domain.file.video.service.S3Service;
import com.back.global.exception.ServiceException;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.back.domain.file.service;
package com.back.domain.file.video.service;

import com.back.domain.file.video.entity.Video;
import com.back.domain.file.video.repository.VideoRepository;
Expand Down