Skip to content

Commit

Permalink
[BE] 파일/폴더를 공유한다. (#27)
Browse files Browse the repository at this point in the history
* fix: MethodArgumentNotValidException Handler 수정

- HandlerMethodValidationException 으로 핸들링 하고 있던 것 변경

* refactor: NO_PERMISSION을 ACCESS_DENIED로 변경

- 중복된 에러 코드 병합

* feat: 공유 링크 생성 기능 추가

- SharedLink Entity 수정 및 Unique 인덱스 추가
- 공유 링크 생성 시 profile 마다 다른 도메인 정보 활용
- Unique한 공유 링크 생성 실패 시 CONFLICT 응답

* feat: 공유 링크 권한 기능 추가

- 공유 링크 생성 시 쓰기 권한, 읽기 권한 설정

* refactor: 공유 링크 엔티티 생성 리팩토링

- SharedLinkFactory 클래스로 엔티티 생성

* refactor: 코드 리뷰 반영

- @ResponseStatus 추가 및 불필요한 공백 제거

* feat: SharedLink 엔티티에 sharedId 필드 추가

- 기존 url 전체를 저장하고 인덱싱해두어서 shareId만으로 조회가 어려움
- sharedId 필드 추가후 팩토리 메서드 및 조회 쿼리 메소드 추가

* fix: shared_uri 쿼리 스트링 key 수정

- 엔티티 이름과 통일하기 위해 shareId -> sharedId로 수정
- 공유 링크 만료 시간 초 기준으로 수정(기존 시간으로 표현하였는데 사용하는 쪽에서 time unit알기 어렵기 때문에 session timeout처럼 초로 표현)

* feat: Folder/FileMetadata에 공유 관련 필드 추가

- 공유 여부 및 공유 만료 시간 필드 추가
- 공유되지 않았다면 LocalDateTime.MIn으로 추가. CommonConstant에 상수 추가

* feat: 폴더/파일 메타데이터에 공유 권한 추가

- PermissionType 필드 추가

* refactor: 변수명 컨벤션에 맞도록 수정

- @value로 주입받는 값이 상수(static final)이 아니기 때문에 대문자 스네이크 케이스 변수명 대신 카멜 케이스 사용

* rename: PermissionType 폴더 이동

- 기존 공유 링크 패키지에서만 사용하던 PermissionType을 엔티티 변경에 따라 폴더, 파일에서도 사용하므로 폴더 이동

* fix: mysql timestamp에서 지원하는 최소 값으로 수정

- 공유되지 않은 폴더 및 파일의 sharingExpiredAt, 폴더 조회시 첫 페이지 조건에 지정할 값
- timestamp에서는 LocalDate.Min() 지원되지 않으므로 지원되는 가장 작은 값인 '1970-1-1'로 지정

* feat: 폴더 조회시 조건 기본 값 넣어주도록 수정

- 공유 링크는 폴더 조회 링크로 리다이렉트해주기 때문에 기본값 필요.

* feat: 상대경로로 서버 host 정보 포함한 절대 경로 생성해주는 UrlUtil 추가

- 공유 링크의 redirect url생성 및 폴더 생성 후 location header에서 사용

* feat: SharedLink 엔티티 수정 및 인덱스 추가

- 사용하지 않는 token필드 제거
- shareLinkUrl 대신 redirect Url(공유 링크 접속 시 리다이렉트 해줄 url)
- 이미 생성된 링크 있는지 조회시에 is_file, target_id 조건으로 조회시 속도 높이기 위해 인덱스 추가

* feat: 공유 링크 생성시 비동기로 폴더 및 파일의 공유 상태 수정 기능 추가

- file entity에 updateShareStatus() 편의 메소드 추가
- application event사용해서 비동기로 처리. 새로운 트랜잭션에서 상태 업데이트

* feat: 기존에 생성된 공유 링크 있다면 반환 기능 추가

- 새로 생성하고 상태 update하지 않고 바로 기존 링크 반환해서 응답 속도 높임.

* feat: 공유 취소 서비스 메서드 추가

- 현재 api 없이 서비스 레이어에만 만들어둠. 추후 사용 예정

* feat: 공유 링크로 접속시 해당 폴더나 파일 조회로 리다이렉트 기능 추가

- query string으로 userid추가한 url 생성
- 302 FOUND 응답

* feat: 공유 취소 기능 추가

- application event를 사용해서 비동기로 하위 폴더 및 파일의 공유 상태 수정

* feat: 폴더/파일 생성시 부모 폴더의 권한 상속 기능 추가

- Factory에서 부모 폴더 엔티티 매개 변수로 받아서 정보 추가
- 부모 폴더 생성자가 폴더 및 파일의 owner가 됨. creator는 요청한 유저

* feat: SharedLink 엔티티에 sharedId 필드 추가

- 기존 url 전체를 저장하고 인덱싱해두어서 shareId만으로 조회가 어려움
- sharedId 필드 추가후 팩토리 메서드 및 조회 쿼리 메소드 추가

* fix: shared_uri 쿼리 스트링 key 수정

- 엔티티 이름과 통일하기 위해 shareId -> sharedId로 수정
- 공유 링크 만료 시간 초 기준으로 수정(기존 시간으로 표현하였는데 사용하는 쪽에서 time unit알기 어렵기 때문에 session timeout처럼 초로 표현)

* feat: Folder/FileMetadata에 공유 관련 필드 추가

- 공유 여부 및 공유 만료 시간 필드 추가
- 공유되지 않았다면 LocalDateTime.MIn으로 추가. CommonConstant에 상수 추가

* feat: 폴더/파일 메타데이터에 공유 권한 추가

- PermissionType 필드 추가

* refactor: 변수명 컨벤션에 맞도록 수정

- @value로 주입받는 값이 상수(static final)이 아니기 때문에 대문자 스네이크 케이스 변수명 대신 카멜 케이스 사용

* rename: PermissionType 폴더 이동

- 기존 공유 링크 패키지에서만 사용하던 PermissionType을 엔티티 변경에 따라 폴더, 파일에서도 사용하므로 폴더 이동

* fix: mysql timestamp에서 지원하는 최소 값으로 수정

- 공유되지 않은 폴더 및 파일의 sharingExpiredAt, 폴더 조회시 첫 페이지 조건에 지정할 값
- timestamp에서는 LocalDate.Min() 지원되지 않으므로 지원되는 가장 작은 값인 '1970-1-1'로 지정

* feat: 폴더 조회시 조건 기본 값 넣어주도록 수정

- 공유 링크는 폴더 조회 링크로 리다이렉트해주기 때문에 기본값 필요.

* feat: 상대경로로 서버 host 정보 포함한 절대 경로 생성해주는 UrlUtil 추가

- 공유 링크의 redirect url생성 및 폴더 생성 후 location header에서 사용

* feat: SharedLink 엔티티 수정 및 인덱스 추가

- 사용하지 않는 token필드 제거
- shareLinkUrl 대신 redirect Url(공유 링크 접속 시 리다이렉트 해줄 url)
- 이미 생성된 링크 있는지 조회시에 is_file, target_id 조건으로 조회시 속도 높이기 위해 인덱스 추가

* feat: 공유 링크 생성시 비동기로 폴더 및 파일의 공유 상태 수정 기능 추가

- file entity에 updateShareStatus() 편의 메소드 추가
- application event사용해서 비동기로 처리. 새로운 트랜잭션에서 상태 업데이트

* feat: 기존에 생성된 공유 링크 있다면 반환 기능 추가

- 새로 생성하고 상태 update하지 않고 바로 기존 링크 반환해서 응답 속도 높임.

* feat: 공유 취소 서비스 메서드 추가

- 현재 api 없이 서비스 레이어에만 만들어둠. 추후 사용 예정

* feat: 공유 링크로 접속시 해당 폴더나 파일 조회로 리다이렉트 기능 추가

- query string으로 userid추가한 url 생성
- 302 FOUND 응답

* feat: 공유 취소 기능 추가

- application event를 사용해서 비동기로 하위 폴더 및 파일의 공유 상태 수정

* feat: 폴더/파일 생성시 부모 폴더의 권한 상속 기능 추가

- Factory에서 부모 폴더 엔티티 매개 변수로 받아서 정보 추가
- 부모 폴더 생성자가 폴더 및 파일의 owner가 됨. creator는 요청한 유저

* feat: 권한 검증에서 사용할 어노테이션 추가

- 권한 인증을 위해 사용하는 필드를 확인하기 위해 CheckField 어노테이션 추가
- DTO로 요청 데이터를 처리하기 위해 CheckDto 어노테이션을 추가
  - DTO 내부의 필드는 CheckField 어노테이션을 사용하여 권한에 사용되는 필드 구분

* feat: 권한이 필요한 컨트롤러 구분을 위한 RequestType 어노테이션 추가

- 읽기, 쓰기 중 어떤 권한이 필요한지 명시하여 그에 맞게 권한을 처리
- File에 대한 작업인지, Folder에 대한 작업인지 명시하여 다른 권한 부여 로직 사용

* feat: AOP 사용 시 컨트롤러의 파라미터를 저장할 DTO 추가

* feat: 권한을 검증하는 Handler 추가

- 파일 타입, 권한 타입, 파라미터 정보를 바탕으로 권한 검증
  - 만료 기간, 쓰기나 읽기 권한 등을 비교하여 검증 진행
  - 파일 이동의 경우 이동할 폴더의 권한까지 함께 확인

* test: 권한을 검증하는 PermissionHandler 테스트 코드 추가

* feat: AOP에 사용할 필드 타입 추가

* feat: 권한 검증에 사용하는 에러 코드 추가

* feat: shared lock으로 파일을 조회하는 메소드 추가

- 권한 검증 중 변경되거나 삭제되면 안되니 읽기는 가능한 shared lock으로 조회

* feat: 파일 타입 enum 추가

- 권한 검증에 파일인지 폴더인지 구분하기 위해 사용

* feat: shared lock으로 폴더를 조회하는 메소드 추가

- 권한 검증 시 폴더의 상태가 변경되면 안되므로 읽기가 가능한 shared lock 사용

* feat: 권한 검증 AOP 추가

- 컨트롤러의 작업 수행 전에 파일과 폴더에 대한 검증을 진행
- 권한 검증이 완료되면 userId와 ownerId에 대한 값을 변경하여 파라미터에 적용
  - 공유된 파일의 경우, 타 사용자의 userId를 ownerId로 변경
  - 생성한 사용자를 구분하기 위해 creatorId에 타 사용자의 id를 추가

* feat: 권한 검증이 필요한 컨트롤러에 AOP 적용

- RequestType, CheckDto, CheckField 어노테이션 추가

* feat: 권한 검증이 필요한 DTO에 어노테이션 추가

- CheckField 어노테이션 추가

* feat: creatorId 필드 추가

- 파일 및 폴더를 추가한 사용자와 그에 대한 권한을 가진 사용자 구분하기 위해 추가

* feat: 파일 업로드 기능에 권한 검증 로직 추가

- 파싱을 하기 때문에 PermissionHandler를 별도로 호출하여 검증 진행

* feat: creatorId, ownerId를 구분하여 저장

- 공유 기능으로 파일 소유자와 생성자를 별도로 구분하여 저장

* feat: 동기로 하위 폴더/파일의 공유 상태 수정

- 비동기로 진행하던 것을 동기로 변경
- 공유 상태 업데이트 실패시 실패 응답 내려주기 위해서

* feat: string.format 대신 문자열 더하기 연산 사용

- string.format은 메모리 사용률 늘어나기 때문에 수정

* feat: 재귀 쿼리 수정

- CTE 쿼리 사용하던 것에서 어플리케이션에서 dfs 수행하는 쿼리로 수정

* refactor: SharedLinkService에서 다른 서비스에 의존하지 않도록 수정

순환 참조 방지

* refactor: dfs시 레코드에 lock걸로 batch update

- select  -> select for update로 수정
- id 모아서 한번에 batch update수정

* fix: 메서드 명 수정

* refactor: 코드 리뷰 반영

- switch case문 변경
- 불필요한 출력문 제거

---------

Co-authored-by: seungh1024 <[email protected]>
  • Loading branch information
i960107 and seungh1024 authored Aug 23, 2024
1 parent b696638 commit 4a058e9
Show file tree
Hide file tree
Showing 42 changed files with 1,320 additions and 133 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@
import com.woowacamp.storage.domain.file.dto.FileMoveDto;
import com.woowacamp.storage.domain.file.service.FileService;
import com.woowacamp.storage.domain.folder.service.FolderService;
import com.woowacamp.storage.global.annotation.CheckDto;
import com.woowacamp.storage.global.annotation.CheckField;
import com.woowacamp.storage.global.annotation.RequestType;
import com.woowacamp.storage.global.aop.type.FieldType;
import com.woowacamp.storage.global.aop.type.FileType;
import com.woowacamp.storage.global.constant.PermissionType;

import lombok.RequiredArgsConstructor;

Expand All @@ -24,15 +30,19 @@ public class FileController {
private final FileService fileService;
private final FolderService folderService;

@RequestType(permission = PermissionType.WRITE, fileType = FileType.FILE)
@PatchMapping("/{fileId}")
public void moveFile(@PathVariable Long fileId, @RequestBody FileMoveDto dto) {
public void moveFile(@CheckField(FieldType.FILE_ID) @PathVariable Long fileId,
@CheckDto @RequestBody FileMoveDto dto) {
fileService.getFileMetadataBy(fileId, dto.userId());
fileService.moveFile(fileId, dto);
}

@RequestType(permission = PermissionType.WRITE, fileType = FileType.FILE)
@DeleteMapping("/{fileId}")
@ResponseStatus(HttpStatus.OK)
public void delete(@PathVariable Long fileId, @RequestParam Long userId) {
public void delete(@CheckField(FieldType.FILE_ID) @PathVariable Long fileId,
@CheckField(FieldType.USER_ID) @RequestParam Long userId) {
fileService.deleteFile(fileId, userId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,13 @@
import com.woowacamp.storage.domain.file.service.FileService;
import com.woowacamp.storage.domain.file.service.FileWriterThreadPool;
import com.woowacamp.storage.domain.file.service.S3FileService;
import com.woowacamp.storage.global.annotation.CheckField;
import com.woowacamp.storage.global.annotation.RequestType;
import com.woowacamp.storage.global.aop.PermissionFieldsDto;
import com.woowacamp.storage.global.aop.PermissionHandler;
import com.woowacamp.storage.global.aop.type.FieldType;
import com.woowacamp.storage.global.aop.type.FileType;
import com.woowacamp.storage.global.constant.PermissionType;
import com.woowacamp.storage.global.error.ErrorCode;

import jakarta.servlet.http.HttpServletRequest;
Expand All @@ -55,15 +62,16 @@ public class MultipartFileController {
private final FileWriterThreadPool fileWriterThreadPool;
private final FileMetadataRepository fileMetadataRepository;
private final FileService fileService;
private final PermissionHandler permissionHandler;

@Value("${cloud.aws.credentials.bucketName}")
private String BUCKET_NAME;
private String bucketName;
@Value("${file.reader.bufferSize}")
private int BUFFER_SIZE;
private int bufferSize;
@Value("${file.reader.lineBufferMaxSize}")
private int LINE_BUFFER_MAX_SIZE;
private int lineBufferMaxSize;
@Value("${file.reader.chunkSize}")
private int S3_CHUNK_SIZE;
private int s3ChunkSize;

/**
* MultipartFile은 임시 저장을 해서 직접 request를 통해 multipart/form-data를 파싱했습니다.
Expand All @@ -86,7 +94,7 @@ public void handleFileUpload(HttpServletRequest request) throws Exception {
* processBuffer 메소드에서 헤더 파싱을 하고 boundary 체크를 하여 각 파트를 구분합니다.
*/
private void processMultipartData(InputStream inputStream, UploadContext context) throws Exception {
byte[] buffer = new byte[BUFFER_SIZE];
byte[] buffer = new byte[bufferSize];
ByteArrayOutputStream lineBuffer = new ByteArrayOutputStream();
ByteArrayOutputStream contentBuffer = new ByteArrayOutputStream();
PartContext partContext = new PartContext();
Expand Down Expand Up @@ -148,8 +156,21 @@ private boolean processBuffer(byte[] buffer, int bytesRead, ByteArrayOutputStrea
// boundary 읽은 이후
processHeader(line, partContext);
if (!partContext.isInHeader() && partContext.getCurrentFileName() != null) {
FileMetadataDto fileMetadataDto = s3FileService.createInitialMetadata(
FormMetadataDto.of(context.getFormFields()), partContext);
FormMetadataDto formMetadataDto = FormMetadataDto.of(context.getFormFields());
// PermissionHandler로 접근 권한을 확인한다.
PermissionFieldsDto permissionFieldsDto = new PermissionFieldsDto();
long userId = formMetadataDto.getUserId();
long parentFolderId = formMetadataDto.getParentFolderId();
permissionFieldsDto.setUserId(userId);
permissionFieldsDto.setFolderId(parentFolderId);
// 파일 쓰기는 현재 파일이 존재하지 않으므로 폴더에 대한 권한을 검증하고 통과하면 ownerId를 받아온다.
long ownerId = permissionHandler.getOwnerIdAndCheckPermission(
PermissionType.WRITE, FileType.FOLDER,
permissionFieldsDto);
formMetadataDto.setUserId(ownerId);
formMetadataDto.setCreatorId(userId);

FileMetadataDto fileMetadataDto = s3FileService.createInitialMetadata(formMetadataDto, partContext);
context.updateFileMetadata(fileMetadataDto);
context.updateIsFileRead();
partContext.setUploadFileName(fileMetadataDto.uuid());
Expand Down Expand Up @@ -220,7 +241,7 @@ private InitiateMultipartUploadResult initializeFileUpload(String fileName, Stri
fileWriterThreadPool.initializePartCount(fileName);
ObjectMetadata metadata = new ObjectMetadata();
metadata.setContentType(contentType);
InitiateMultipartUploadRequest initRequest = new InitiateMultipartUploadRequest(BUCKET_NAME,
InitiateMultipartUploadRequest initRequest = new InitiateMultipartUploadRequest(bucketName,
fileName).withObjectMetadata(metadata);

return amazonS3.initiateMultipartUpload(initRequest);
Expand All @@ -232,7 +253,7 @@ private InitiateMultipartUploadResult initializeFileUpload(String fileName, Stri
private void processContent(ByteArrayOutputStream contentBuffer, ByteArrayOutputStream lineBuffer,
PartContext partContext, UploadState state) throws Exception {
contentBuffer.write(lineBuffer.toByteArray());
if (partContext.getCurrentFileName() != null && contentBuffer.size() >= S3_CHUNK_SIZE) {
if (partContext.getCurrentFileName() != null && contentBuffer.size() >= s3ChunkSize) {
uploadChunk(contentBuffer, state, partContext);
}
}
Expand All @@ -257,11 +278,11 @@ private void uploadChunk(ByteArrayOutputStream contentBuffer, UploadState state,
*/
private void checkLineBufferSize(ByteArrayOutputStream lineBuffer, ByteArrayOutputStream contentBuffer,
PartContext partContext, UploadState state) throws Exception {
if (lineBuffer.size() >= LINE_BUFFER_MAX_SIZE) {
if (lineBuffer.size() >= lineBufferMaxSize) {
contentBuffer.write(lineBuffer.toByteArray());
lineBuffer.reset();

if (contentBuffer.size() >= S3_CHUNK_SIZE) {
if (contentBuffer.size() >= s3ChunkSize) {
uploadChunk(contentBuffer, state, partContext);
}
}
Expand Down Expand Up @@ -321,13 +342,14 @@ private String extractAttribute(String source, String attribute) {
return null;
}

@RequestType(permission = PermissionType.READ, fileType = FileType.FILE)
@GetMapping("/download/{fileId}")
@Validated
ResponseEntity<InputStreamResource> download(@PathVariable Long fileId,
@Positive(message = "올바른 입력값이 아닙니다.") @RequestParam("userId") Long userId) {
ResponseEntity<InputStreamResource> download(@CheckField(FieldType.FILE_ID) @PathVariable Long fileId,
@CheckField(FieldType.USER_ID) @Positive(message = "올바른 입력값이 아닙니다.") @RequestParam("userId") Long userId) {

FileMetadata fileMetadata = fileService.getFileMetadataBy(fileId, userId);
FileDataDto fileDataDto = s3FileService.downloadByS3(fileId, BUCKET_NAME, fileMetadata.getUuidFileName());
FileDataDto fileDataDto = s3FileService.downloadByS3(fileId, bucketName, fileMetadata.getUuidFileName());
HttpHeaders headers = new HttpHeaders();
// HTTP 응답 헤더에 Content-Type 설정
String fileType = fileMetadata.getFileType();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package com.woowacamp.storage.domain.file.dto;

import com.woowacamp.storage.global.annotation.CheckField;
import com.woowacamp.storage.global.aop.type.FieldType;

public record FileMoveDto(
long targetFolderId,
long userId
@CheckField(FieldType.MOVE_FOLDER_ID) long targetFolderId,
@CheckField(FieldType.USER_ID) long userId
) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,21 @@ public class FormMetadataDto {
private long userId;
private long parentFolderId;
private long fileSize;
private long creatorId;

public FormMetadataDto(long userId, long parentFolderId, long fileSize) {
this.userId = userId;
this.parentFolderId = parentFolderId;
this.fileSize = fileSize;
}

public void setUserId(long userId) {
this.userId = userId;
}

public void setCreatorId(long creatorId) {
this.creatorId = creatorId;
}

public static FormMetadataDto of(Map<String, String> formFields) {
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import java.time.LocalDateTime;

import com.woowacamp.storage.global.constant.CommonConstant;
import com.woowacamp.storage.global.constant.PermissionType;
import com.woowacamp.storage.global.constant.UploadStatus;

import jakarta.persistence.Column;
Expand Down Expand Up @@ -77,21 +79,19 @@ public class FileMetadata {
@NotNull
private UploadStatus uploadStatus;

@Column(name = "sharing_expired_at", columnDefinition = "TIMESTAMP NOT NULL")
@NotNull
private LocalDateTime sharingExpiredAt;

@Column(name = "permission_type", columnDefinition = "VARCHAR(10) NOT NULL")
@NotNull
@Enumerated(EnumType.STRING)
private PermissionType permissionType;

@Builder
public FileMetadata(
Long id,
Long rootId,
Long creatorId,
Long ownerId,
String fileType,
LocalDateTime createdAt,
LocalDateTime updatedAt,
Long parentFolderId,
Long fileSize,
String uploadFileName,
String uuidFileName,
UploadStatus uploadStatus
) {
public FileMetadata(Long id, Long rootId, Long creatorId, Long ownerId, String fileType, LocalDateTime createdAt,
LocalDateTime updatedAt, Long parentFolderId, Long fileSize, String uploadFileName, String uuidFileName,
UploadStatus uploadStatus, LocalDateTime sharingExpiredAt, PermissionType permissionType) {
this.id = id;
this.rootId = rootId;
this.creatorId = creatorId;
Expand All @@ -104,6 +104,8 @@ public FileMetadata(
this.uploadFileName = uploadFileName;
this.uuidFileName = uuidFileName;
this.uploadStatus = uploadStatus;
this.sharingExpiredAt = sharingExpiredAt;
this.permissionType = permissionType;
}

public void updateCreatedAt(LocalDateTime createdAt) {
Expand All @@ -125,4 +127,17 @@ public void updateFinishUploadStatus() {
public void updateParentFolderId(Long parentFolderId) {
this.parentFolderId = parentFolderId;
}

public void updateShareStatus(PermissionType permissionType, LocalDateTime sharingExpiredAt) {
if (permissionType == null || permissionType.equals(PermissionType.NONE)) {
throw new IllegalArgumentException("잘못된 공유 권한 수정 입니다.");
}
this.permissionType = permissionType;
this.sharingExpiredAt = sharingExpiredAt;
}

public void cancelShare() {
this.permissionType = PermissionType.NONE;
this.sharingExpiredAt = CommonConstant.UNAVAILABLE_TIME;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,29 @@

import java.time.LocalDateTime;

import com.woowacamp.storage.domain.folder.entity.FolderMetadata;
import com.woowacamp.storage.domain.user.entity.User;
import com.woowacamp.storage.global.constant.UploadStatus;

public class FileMetadataFactory {

public static FileMetadata buildInitialMetadata(User user, long parentFolderId, long fileSize, String uuidFileName,
String fileName, String fileType) {
String fileName, String fileType, long creatorId, FolderMetadata parentFolderMetadata) {
LocalDateTime now = LocalDateTime.now();
return FileMetadata.builder()
.rootId(user.getRootFolderId())
.creatorId(user.getId())
.creatorId(creatorId)
.ownerId(user.getId())
.parentFolderId(parentFolderId)
.fileSize(fileSize)
.uuidFileName(uuidFileName)
.uploadStatus(UploadStatus.PENDING)
.uploadFileName(fileName)
.fileType(fileType)
.sharingExpiredAt(parentFolderMetadata.getSharingExpiredAt())
.createdAt(now)
.updatedAt(now)
.permissionType(parentFolderMetadata.getPermissionType())
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,12 @@

import com.woowacamp.storage.domain.file.entity.FileMetadata;
import com.woowacamp.storage.domain.folder.dto.FolderContentsSortField;
import com.woowacamp.storage.global.constant.PermissionType;

public interface FileCustomRepository {
List<FileMetadata> selectFilesWithPagination(long parentId, long cursorId, FolderContentsSortField sortBy,
Sort.Direction direction, int limit, LocalDateTime time, Long size);

void updateShareStatusInBatch(List<Long> folderIdsToUpdate, PermissionType permissionType,
LocalDateTime unavailableTime);
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import com.woowacamp.storage.domain.file.entity.FileMetadata;
import com.woowacamp.storage.domain.file.entity.QFileMetadata;
import com.woowacamp.storage.domain.folder.dto.FolderContentsSortField;
import com.woowacamp.storage.global.constant.PermissionType;
import com.woowacamp.storage.global.constant.UploadStatus;

import lombok.RequiredArgsConstructor;
Expand Down Expand Up @@ -75,4 +76,14 @@ public List<FileMetadata> selectFilesWithPagination(long parentId, long cursorId

return query.fetch();
}

@Override
public void updateShareStatusInBatch(List<Long> fileIdsToUpdate, PermissionType permissionType,
LocalDateTime sharingExpireAt) {
queryFactory.update(fileMetadata)
.set(fileMetadata.permissionType, permissionType)
.set(fileMetadata.sharingExpiredAt, sharingExpireAt)
.where(fileMetadata.id.in(fileIdsToUpdate))
.execute();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -82,4 +82,11 @@ int finalizeMetadata(@Param("fileId") long fileId, @Param("fileSize") long fileS
void setMetadataStatusFail(@Param("fileId") long fileId, @Param("uploadStatus") UploadStatus uploadStatus);

boolean existsByParentFolderIdAndUploadStatus(Long parentFolderId, UploadStatus uploadStatus);

@Lock(LockModeType.PESSIMISTIC_READ)
@Query(value = """
select f from FileMetadata f
where f.id = :fileId
""")
Optional<FileMetadata> findByIdForShare(@Param("fileId") long fileId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ public FileMetadata getFileMetadataBy(Long fileId, Long userId) {
FileMetadata fileMetadata = fileMetadataRepository.findById(fileId)
.orElseThrow(ErrorCode.FILE_NOT_FOUND::baseException);

if (!Objects.equals(fileMetadata.getCreatorId(), userId)) {
if (!Objects.equals(fileMetadata.getOwnerId(), userId)) {
throw ACCESS_DENIED.baseException();
}
return fileMetadata;
Expand All @@ -90,4 +90,5 @@ public void deleteFile(Long fileId, Long userId) {
throw ErrorCode.FILE_DELETE_FAILED.baseException();
}
}

}
Loading

0 comments on commit 4a058e9

Please sign in to comment.