Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
2ece31a
refactor(DocumentController): url 변경
Chan531 Nov 19, 2024
543fa14
chore(DocumentController): created URI 수정
Chan531 Nov 19, 2024
2450ed9
chore(DocumentController): folderId 타입 수정
Chan531 Nov 19, 2024
57f419e
feat(DocumentService): 폴더 검증 로직 추가
Chan531 Nov 19, 2024
5e3865a
feat(DocumentService): 파일 이름 중복 검증 로직 추가
Chan531 Nov 19, 2024
44c0260
feat(ErrorCode): 중복된 파일 이름 관련 에러 코드 추가
Chan531 Nov 19, 2024
1d57b96
feat(DocumentService): 파일 이름 중복 여부 검증
Chan531 Nov 19, 2024
0e0af79
chore(DocumentService): 미사용 메소드 제거
Chan531 Nov 19, 2024
1880878
feat(Folder): 팀 아이디 검증 기능 추가
Chan531 Nov 19, 2024
35acb3c
refactor(DocumentService): 팀 아이디 검증 책임 변경
Chan531 Nov 19, 2024
2241f3f
refactor(DocumentController): folderId 파라미터 추가
Chan531 Nov 19, 2024
dec324a
refactor(DocumentService): folderId 파라미터 적용
Chan531 Nov 19, 2024
1601f99
refactor(DocumentService): 파일 아이디 리스트 생성 로직 메소드 분리
Chan531 Nov 19, 2024
6a57b01
style(DocumentService): 메소드 선언 순서 변경
Chan531 Nov 19, 2024
739d99d
refactor(DocumentsCreateRequest): 폴더 id 요청 제거
Chan531 Nov 19, 2024
728eccc
refactor(FolderController): url 변경
Chan531 Nov 19, 2024
83d625c
style(DocumentController): 미사용 import문 제거
Chan531 Nov 19, 2024
1d93bdd
refactor(FolderController): teamId 수정
Chan531 Nov 19, 2024
4d56713
refactor(FolderController): 폴더 id 파라미터 추가
Chan531 Nov 19, 2024
37c005a
refactor(FolderService): 폴더 찾기 시, 팀 아이디 검증 로직 추가
Chan531 Nov 19, 2024
9338065
refactor(FolderController): url에서 path 제거
Chan531 Nov 19, 2024
28400df
refactor(FolderService): path 전달 방식 수정
Chan531 Nov 19, 2024
b9561b4
refactor(FolderCreateRequest): 부모 폴더 id 제거
Chan531 Nov 19, 2024
f521403
refactor(FolderService): 폴더 path 추출 로직 메소드 분리
Chan531 Nov 19, 2024
aa0600b
feat(Folder): 자식 폴더 path 반환
Chan531 Nov 19, 2024
f50af72
feat(ErrorCode): 중복된 폴더 이름 관련 에러 코드 추가
Chan531 Nov 19, 2024
1056a11
feat(FolderService): 폴더 이름 중복 여부 검증
Chan531 Nov 19, 2024
af6c18d
feat(ErrorHandler): FolderException 추가
Chan531 Nov 19, 2024
8e7e44c
chore(ErrorHandler): 메소드명 수정
Chan531 Nov 19, 2024
6bab42d
fix(FolderService): 자식 주소를 반환하도록 수정
Chan531 Nov 19, 2024
45d23c3
feat(Constant): 구분자 상수 추가
Chan531 Nov 19, 2024
c9c53db
refactor(Folder): 상수 적용
Chan531 Nov 19, 2024
1f2c80f
refactor(FolderService): getFolder, getFolderPath 중복 코드 삭제
Chan531 Nov 19, 2024
c1d186c
refactor(FolderService): 메소드명 수정
Chan531 Nov 19, 2024
9423c57
refactor(DocumentController): 파일 업로드 응답 제거
Chan531 Nov 19, 2024
a67e400
refactor(DocumentService): 파일 업로드 응답 제거
Chan531 Nov 19, 2024
f2b413e
chore(FolderService): 메소드 네이밍 변경 적용
Chan531 Nov 20, 2024
3b03318
chore(DocumentService): 메소드 네이밍 변경 적용
Chan531 Nov 20, 2024
9a0cf94
chore(DocumentService): 파라미터 final 키워드 적용
Chan531 Nov 20, 2024
256e0d9
chore(Folder): 파라미터 final 키워드 적용
Chan531 Nov 20, 2024
357b0db
chore(DocumentController): 파라미터 final 키워드 적용
Chan531 Nov 20, 2024
249d188
chore(FolderController): 파라미터 final 키워드 적용
Chan531 Nov 20, 2024
a0f350a
chore(FolderFinder): 파라미터 final 키워드 적용
Chan531 Nov 20, 2024
d6a2eb8
chore(FolderSaver): 파리미터 final 키워드 적용
Chan531 Nov 20, 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
14 changes: 11 additions & 3 deletions src/main/java/com/tiki/server/common/handler/ErrorHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.tiki.server.auth.exception.AuthException;
import com.tiki.server.common.dto.ErrorCodeResponse;
import com.tiki.server.emailverification.exception.EmailVerificationException;
import com.tiki.server.folder.exception.FolderException;
import com.tiki.server.note.exception.NoteException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
Expand Down Expand Up @@ -79,14 +80,21 @@ public ResponseEntity<BaseResponse> externalException(ExternalException exceptio
}

@ExceptionHandler(EmailVerificationException.class)
public ResponseEntity<BaseResponse> MailException(EmailVerificationException exception) {
public ResponseEntity<BaseResponse> mailException(EmailVerificationException exception) {
log.error(exception.getMessage());
val errorCode = exception.getErrorCode();
return ResponseEntity.status(errorCode.getHttpStatus()).body(ErrorResponse.of(errorCode.getMessage()));
}

@ExceptionHandler(FolderException.class)
public ResponseEntity<BaseResponse> folderException(FolderException exception) {
log.error(exception.getMessage());
val errorCode = exception.getErrorCode();
return ResponseEntity.status(errorCode.getHttpStatus()).body(ErrorResponse.of(errorCode.getMessage()));
}

@ExceptionHandler(AuthException.class)
public ResponseEntity<BaseResponse> AuthException(AuthException exception) {
public ResponseEntity<BaseResponse> authException(AuthException exception) {
log.error(exception.getMessage());
val errorCode = exception.getErrorCode();
return ResponseEntity.status(errorCode.getHttpStatus()).body(
Expand All @@ -101,7 +109,7 @@ public ResponseEntity<BaseResponse> httpMessageNotReadableException(HttpMessageN
}

@ExceptionHandler(Exception.class)
public ResponseEntity<BaseResponse> Exception(Exception exception) {
public ResponseEntity<BaseResponse> exception(Exception exception) {
log.error(exception.getMessage());
val errorCode = UNCAUGHT_SERVER_EXCEPTION;
return ResponseEntity.status(errorCode.getHttpStatus()).body(ErrorResponse.of(errorCode.getMessage()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
Expand All @@ -36,9 +35,9 @@ public class DocumentController implements DocumentControllerDocs {
@Override
@GetMapping("/documents/team/{teamId}/timeline")
public ResponseEntity<SuccessResponse<DocumentsGetResponse>> getAllDocuments(
Principal principal,
@PathVariable long teamId,
@RequestParam String type
final Principal principal,
@PathVariable final long teamId,
@RequestParam final String type
) {
long memberId = Long.parseLong(principal.getName());
DocumentsGetResponse response = documentService.getAllDocuments(memberId, teamId, type);
Expand All @@ -48,32 +47,33 @@ public ResponseEntity<SuccessResponse<DocumentsGetResponse>> getAllDocuments(
@Override
@DeleteMapping("/documents/team/{teamId}/document/{documentId}")
public ResponseEntity<?> deleteDocument(
Principal principal,
@PathVariable long teamId,
@PathVariable long documentId
final Principal principal,
@PathVariable final long teamId,
@PathVariable final long documentId
) {
long memberId = Long.parseLong(principal.getName());
documentService.deleteDocument(memberId, teamId, documentId);
return ResponseEntity.noContent().build();
}

@PostMapping("/documents")
public ResponseEntity<SuccessResponse<DocumentsCreateResponse>> createDocuments(
Principal principal,
@RequestHeader("team-id") long teamId,
@RequestBody DocumentsCreateRequest request
@PostMapping("/teams/{teamId}/documents")
public ResponseEntity<SuccessResponse<?>> createDocuments(
final Principal principal,
@PathVariable final long teamId,
@RequestParam(required = false) final Long folderId,
@RequestBody final DocumentsCreateRequest request
) {
long memberId = Long.parseLong(principal.getName());
DocumentsCreateResponse response = documentService.createDocuments(memberId, teamId, request);
return ResponseEntity.created(UriGenerator.getUri("api/v1/documents"))
.body(SuccessResponse.success(SUCCESS_CREATE_DOCUMENTS.getMessage(), response));
documentService.createDocuments(memberId, teamId, folderId, request);
return ResponseEntity.created(UriGenerator.getUri("teams/" + teamId + "/documents"))
.body(SuccessResponse.success(SUCCESS_CREATE_DOCUMENTS.getMessage()));
}

@GetMapping("/teams/{teamId}/documents")
public ResponseEntity<SuccessResponse<DocumentsGetResponse>> getDocuments(
final Principal principal,
@PathVariable long teamId,
@RequestParam(required = false) Long folderId
@PathVariable final long teamId,
@RequestParam(required = false) final Long folderId
) {
long memberId = Long.parseLong(principal.getName());
DocumentsGetResponse response = documentService.get(memberId, teamId, folderId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import java.util.List;

public record DocumentsCreateRequest(
List<DocumentCreateRequest> documents,
Long folderId
List<DocumentCreateRequest> documents
) {
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.tiki.server.document.message;

import static org.springframework.http.HttpStatus.BAD_REQUEST;
import static org.springframework.http.HttpStatus.CONFLICT;
import static org.springframework.http.HttpStatus.FORBIDDEN;
import static org.springframework.http.HttpStatus.NOT_FOUND;

Expand All @@ -20,7 +21,10 @@ public enum ErrorCode {
INVALID_AUTHORIZATION(FORBIDDEN, "문서에 대한 권한이 없습니다."),

/* 404 NOT_FOUND : 자원을 찾을 수 없음 */
INVALID_DOCUMENT(NOT_FOUND, "유효하지 않은 문서입니다.");
INVALID_DOCUMENT(NOT_FOUND, "유효하지 않은 문서입니다."),

/* 409 CONFLICT : 중복된 자원 */
DOCUMENT_NAME_DUPLICATE(CONFLICT, "중복된 파일 이름입니다.");

private final HttpStatus httpStatus;
private final String message;
Expand Down
50 changes: 35 additions & 15 deletions src/main/java/com/tiki/server/document/service/DocumentService.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.tiki.server.document.service;

import static com.tiki.server.document.message.ErrorCode.DOCUMENT_NAME_DUPLICATE;

import java.util.List;

import org.springframework.stereotype.Service;
Expand All @@ -11,10 +13,11 @@
import com.tiki.server.document.adapter.DocumentSaver;
import com.tiki.server.document.dto.request.DocumentCreateRequest;
import com.tiki.server.document.dto.request.DocumentsCreateRequest;
import com.tiki.server.document.dto.response.DocumentsCreateResponse;
import com.tiki.server.document.dto.response.DocumentsGetResponse;
import com.tiki.server.document.entity.Document;
import com.tiki.server.document.exception.DocumentException;
import com.tiki.server.folder.adapter.FolderFinder;
import com.tiki.server.folder.entity.Folder;
import com.tiki.server.memberteammanager.adapter.MemberTeamManagerFinder;
import com.tiki.server.memberteammanager.entity.MemberTeamManager;

Expand All @@ -31,52 +34,69 @@ public class DocumentService {
private final FolderFinder folderFinder;
private final MemberTeamManagerFinder memberTeamManagerFinder;

public DocumentsGetResponse getAllDocuments(long memberId, long teamId, String type) {
public DocumentsGetResponse getAllDocuments(final long memberId, final long teamId, final String type) {
MemberTeamManager memberTeamManager = memberTeamManagerFinder.findByMemberIdAndTeamIdOrElseThrow(memberId, teamId);
Position accessiblePosition = Position.getAccessiblePosition(type);
memberTeamManager.checkMemberAccessible(accessiblePosition);
return getAllDocumentsByType(teamId, accessiblePosition);
}

@Transactional
public void deleteDocument(long memberId, long teamId, long documentId) {
public void deleteDocument(final long memberId, final long teamId, final long documentId) {
MemberTeamManager memberTeamManager = memberTeamManagerFinder.findByMemberIdAndTeamIdOrElseThrow(memberId, teamId);
Document document = documentFinder.findByIdWithTimeBlock(documentId);
memberTeamManager.checkMemberAccessible(document.getTimeBlock().getAccessiblePosition());
documentDeleter.delete(document);
}

@Transactional
public DocumentsCreateResponse createDocuments(long memberId, long teamId, DocumentsCreateRequest request) {
public void createDocuments(final long memberId, final long teamId,
final Long folderId, final DocumentsCreateRequest request) {
memberTeamManagerFinder.findByMemberIdAndTeamIdOrElseThrow(memberId, teamId);
checkFolderIsExist(request.folderId());
List<Long> documentIds = request.documents().stream()
.map(document -> saveDocument(teamId, request.folderId(), document).getId())
.toList();
return DocumentsCreateResponse.from(documentIds);
validateFolder(folderId, teamId);
validateFileName(folderId, teamId, request);
saveDocuments(teamId, folderId, request);
}

public DocumentsGetResponse get(final long memberId, final long teamId, final Long folderId) {
memberTeamManagerFinder.findByMemberIdAndTeamId(memberId, teamId);
memberTeamManagerFinder.findByMemberIdAndTeamIdOrElseThrow(memberId, teamId);
List<Document> documents = documentFinder.findByTeamIdAndFolderId(teamId, folderId);
return DocumentsGetResponse.from(documents);
}

private DocumentsGetResponse getAllDocumentsByType(long teamId, Position accessiblePosition) {
private DocumentsGetResponse getAllDocumentsByType(final long teamId, final Position accessiblePosition) {
List<Document> documents = documentFinder.findAllByTeamIdAndAccessiblePosition(teamId, accessiblePosition);
return DocumentsGetResponse.from(documents);
}

private void checkFolderIsExist(Long folderId) {
private void validateFolder(final Long folderId, final long teamId) {
if (folderId == null) {
return;
}
folderFinder.findById(folderId);
Folder folder = folderFinder.findById(folderId);
folder.validateTeamId(teamId);
}

private void validateFileName(final Long folderId, final long teamId, final DocumentsCreateRequest request) {
List<Document> documents = documentFinder.findByTeamIdAndFolderId(teamId, folderId);
for (Document document : documents) {
checkFileNameIsDuplicated(document.getFileName(), request);
}
}

private void checkFileNameIsDuplicated(final String fileName, final DocumentsCreateRequest request) {
if (request.documents().stream().anyMatch(document -> document.fileName().equals(fileName))) {
throw new DocumentException(DOCUMENT_NAME_DUPLICATE);
}
}

private void saveDocuments(final long teamId, final Long folderId, final DocumentsCreateRequest request) {
request.documents().forEach(document -> saveDocument(teamId, folderId, document));
}

private Document saveDocument(long teamId, Long folderId, DocumentCreateRequest request) {
private void saveDocument(final long teamId, final Long folderId, final DocumentCreateRequest request) {
Document document = Document.of(
request.fileName(), request.fileUrl(), request.capacity(), teamId, folderId);
return documentSaver.save(document);
documentSaver.save(document);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public class FolderFinder {

private final FolderRepository folderRepository;

public Folder findById(long id) {
public Folder findById(final long id) {
return folderRepository.findById(id)
.orElseThrow(() -> new FolderException(INVALID_FOLDER));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public class FolderSaver {

private final FolderRepository folderRepository;

public Folder save(Folder folder) {
public Folder save(final Folder folder) {
return folderRepository.save(folder);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@
public class Constant {

public static final String ROOT_PATH = "";
public static final String SEPARATOR = "/";
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
Expand All @@ -27,30 +27,31 @@

@RestController
@RequiredArgsConstructor
@RequestMapping("api/v1/folders")
@RequestMapping("api/v1")
public class FolderController {

private final FolderService folderService;

@GetMapping()
@GetMapping("/teams/{teamId}/folders")
public ResponseEntity<SuccessResponse<FoldersGetResponse>> getFolders(
final Principal principal,
@RequestHeader("team-id") long teamId,
@RequestParam(defaultValue = ROOT_PATH) String path
@PathVariable final long teamId,
@RequestParam(required = false) final Long folderId
) {
long memberId = Long.parseLong(principal.getName());
FoldersGetResponse response = folderService.get(memberId, teamId, path);
FoldersGetResponse response = folderService.get(memberId, teamId, folderId);
return ResponseEntity.ok(success(SUCCESS_GET_FOLDERS.getMessage(), response));
}

@PostMapping()
@PostMapping("/teams/{teamId}/folders")
public ResponseEntity<SuccessResponse<FolderCreateResponse>> createFolder(
Principal principal,
@RequestHeader("team-id") long teamId,
@RequestBody FolderCreateRequest request
@PathVariable final long teamId,
@RequestParam(required = false) final Long folderId,
@RequestBody final FolderCreateRequest request
) {
long memberId = Long.parseLong(principal.getName());
FolderCreateResponse response = folderService.create(memberId, teamId, request);
FolderCreateResponse response = folderService.create(memberId, teamId, folderId, request);
return ResponseEntity.created(UriGenerator.getUri("api/v1/folders/" + response.folderId()))
.body(success(SUCCESS_CREATE_FOLDER.getMessage(), response));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import lombok.NonNull;

public record FolderCreateRequest(
@NonNull String name,
Long parentId
@NonNull String name
) {
}
22 changes: 18 additions & 4 deletions src/main/java/com/tiki/server/folder/entity/Folder.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
package com.tiki.server.folder.entity;

import static com.tiki.server.document.message.ErrorCode.INVALID_AUTHORIZATION;
import static com.tiki.server.folder.constant.Constant.ROOT_PATH;
import static com.tiki.server.folder.constant.Constant.SEPARATOR;
import static jakarta.persistence.GenerationType.IDENTITY;

import com.tiki.server.common.entity.BaseTime;
import com.tiki.server.document.exception.DocumentException;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
Expand All @@ -25,16 +29,26 @@ public class Folder extends BaseTime {

private long teamId;

public Folder(String name, Folder parentFolder, long teamId) {
public Folder(final String name, final Folder parentFolder, final long teamId) {
this.name = name;
this.path = generatePath(parentFolder);
this.teamId = teamId;
}

private String generatePath(Folder parentFolder) {
public void validateTeamId(final long teamId) {
if (this.teamId != teamId) {
throw new DocumentException(INVALID_AUTHORIZATION);
}
}

public String getChildPath() {
return path + SEPARATOR + id;
}

private String generatePath(final Folder parentFolder) {
if (parentFolder == null) {
return "";
return ROOT_PATH;
}
return parentFolder.getPath() + "/" + parentFolder.getId();
return parentFolder.getPath() + SEPARATOR + parentFolder.getId();
}
}
6 changes: 5 additions & 1 deletion src/main/java/com/tiki/server/folder/message/ErrorCode.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.tiki.server.folder.message;

import static org.springframework.http.HttpStatus.CONFLICT;
import static org.springframework.http.HttpStatus.NOT_FOUND;

import org.springframework.http.HttpStatus;
Expand All @@ -12,7 +13,10 @@
public enum ErrorCode {

/* 404 NOT_FOUND : 자원을 찾을 수 없음 */
INVALID_FOLDER(NOT_FOUND, "유효하지 않은 폴더입니다.");
INVALID_FOLDER(NOT_FOUND, "유효하지 않은 폴더입니다."),

/* 409 CONFLICT : 중복된 자원 */
FOLDER_NAME_DUPLICATE(CONFLICT, "중복된 폴더 이름입니다.");;

private final HttpStatus httpStatus;
private final String message;
Expand Down
Loading
Loading