diff --git a/backend/src/main/java/codezap/template/controller/SpringDocTemplateController.java b/backend/src/main/java/codezap/template/controller/SpringDocTemplateController.java index 917dcf96d..e8a94cac3 100644 --- a/backend/src/main/java/codezap/template/controller/SpringDocTemplateController.java +++ b/backend/src/main/java/codezap/template/controller/SpringDocTemplateController.java @@ -11,13 +11,10 @@ import codezap.member.dto.MemberDto; import codezap.template.dto.request.CreateTemplateRequest; import codezap.template.dto.request.UpdateTemplateRequest; -import codezap.template.dto.response.ExploreTemplatesResponse; import codezap.template.dto.response.FindAllTemplatesResponse; import codezap.template.dto.response.FindTemplateResponse; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.headers.Header; -import io.swagger.v3.oas.annotations.media.Content; -import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.security.SecurityRequirement; import io.swagger.v3.oas.annotations.tags.Tag; @@ -28,39 +25,70 @@ public interface SpringDocTemplateController { @SecurityRequirement(name = "쿠키 인증 토큰") @Operation(summary = "템플릿 생성", description = """ 새로운 템플릿을 생성합니다. \n - 템플릿의 제목, 썸네일 스니펫의 순서, 스니펫 목록, 카테고리 ID, 태그 목록이 필요합니다. \n - 스니펫 목록은 파일 이름, 소스 코드, 해당 스니펫의 순서가 필요합니다. \n - * 썸네일 스니펫은 1로 고정입니다. (2024.07.15 기준) \n - * 모든 스니펫 순서는 1부터 시작합니다. \n - * 스니펫 순서는 오름차순으로 정렬하여 보내야 합니다. \n + 템플릿명, 템플릿 설명, 스니펫 목록, 썸네일 스니펫 순서, 카테고리 ID, 태그 목록이 필요합니다. \n + * 템플릿 이름은 비어있거나 공백일 수 없다. + + 스니펫 목록은 파일명, 소스 코드, 스니펫 순서가 필요합니다. \n + * 스니펫 순서는 1부터 시작합니다. + * 스니펫 순서는 오름차순으로 정렬하여 보내야 합니다. """) @ApiResponse(responseCode = "201", description = "템플릿 생성 성공", headers = { @Header(name = "생성된 템플릿의 API 경로", example = "/templates/1")}) @ApiErrorResponse(status = HttpStatus.BAD_REQUEST, instance = "/templates", errorCases = { - @ErrorCase(description = "모든 필드 중 null인 값이 있는 경우", exampleMessage = "템플릿 이름 null 입니다."), - @ErrorCase(description = "제목 또는 스니펫 파일 또는 태그 이름이 255자를 초과한 경우", exampleMessage = "제목은 최대 255자까지 입력 가능합니다."), - @ErrorCase(description = "썸네일 스니펫의 순서가 1이 아닌 경우", exampleMessage = "썸네일 스니펫의 순서가 잘못되었습니다."), + @ErrorCase(description = "모든 필드 중 null인 값이 있는 경우", exampleMessage = "템플릿 설명이 null 입니다."), + @ErrorCase(description = "템플릿명, 스니펫 파일명, 스니펫 소스 코드가 공백일 경우", exampleMessage = "템플릿명이 비어 있거나 공백입니다."), + @ErrorCase(description = "템플릿명, 스니펫 파일명, 태그명이 255자를 초과한 경우", exampleMessage = "템플릿명은 최대 255자까지 입력 가능합니다."), + @ErrorCase(description = "스니펫 소스 코드가 65,535 byte를 초과한 경우", exampleMessage = "소스 코드는 최대 65,535 Byte까지 입력 가능합니다."), @ErrorCase(description = "스니펫 순서가 잘못된 경우", exampleMessage = "스니펫 순서가 잘못되었습니다."), - @ErrorCase(description = "스니펫 내용 65,535 byte를 초과한 경우", exampleMessage = "파일 내용은 최대 65,535 byte까지 입력 가능합니다.") }) - ResponseEntity create(CreateTemplateRequest createTemplateRequest, MemberDto memberDto); + @ApiErrorResponse(status = HttpStatus.UNAUTHORIZED, instance = "/templates/1", errorCases = { + @ErrorCase(description = "인증 정보가 없거나 잘못된 경우", exampleMessage = "인증에 실패했습니다."), + @ErrorCase(description = "카테고리 권한이 없는 경우", exampleMessage = "해당 카테고리에 대한 권한이 없습니다."), + }) + @ApiErrorResponse(status = HttpStatus.NOT_FOUND, instance = "/templates/1", errorCases = { + @ErrorCase(description = "인증 정보에 포함된 멤버가 없는 경우", exampleMessage = "식별자 1에 해당하는 멤버가 존재하지 않습니다."), + @ErrorCase(description = "카테고리가 없는 경우", exampleMessage = "식별자 1에 해당하는 카테고리가 존재하지 않습니다."), + @ErrorCase(description = "이름에 맞는 태그가 없는 경우", exampleMessage = "이름이 tag1인 태그는 존재하지 않습니다."), + @ErrorCase(description = "해당 순서인 스니펫 없는 경우", exampleMessage = "템플릿에 1번째 스니펫이 존재하지 않습니다."), + }) + ResponseEntity createTemplate(MemberDto memberDto, CreateTemplateRequest createTemplateRequest); @SecurityRequirement(name = "쿠키 인증 토큰") @Operation(summary = "템플릿 검색", description = """ - 필터링 조건에 맞는 모든 템플릿을 조회합니다. - - 필터링 조건 + 필터링 조건에 맞는 모든 템플릿을 조회합니다. \n + - 필터링 조건 \n - 멤버 ID - - 검색 키워드 (템플릿 제목, 템플릿 설명, 스니펫 파일명, 소스 코드) + - 검색 키워드 (템플릿명, 템플릿 설명, 스니펫 파일명, 소스 코드) - 카테고리 ID - - 태그 ID - - 정렬 방식 - - 최신순 (createdAt,asc) - - 오래된순 (createdAt,desc) - 조회 조건으로 페이지 인덱스, 한 페이지에 들어갈 최대 템플릿의 개수를 변경할 수 있습니다. - 페이지 인덱스는 1, 템플릿 개수는 20개가 기본 값입니다. + - 태그 ID들 \n + + 페이징 조건을 줄 수 있습니다. 페이지 번호는 1, 템플릿 개수는 20, 정렬 방식은 최신순이 기본 값입니다. \n + - 페이징 조건 \n + - 페이지 번호(page) + - 한 페이지에 템플릿 개수(size) + - 페이지 정렬 방식(sort) \n + + - 정렬 방식 \n + - 최신순 (modifiedAt,asc) + - 오래된순 (modifiedAt,desc) \n """) - @ApiResponse(responseCode = "200", description = "템플릿 단건 조회 성공", - content = {@Content(schema = @Schema(implementation = ExploreTemplatesResponse.class))}) + @ApiResponse(responseCode = "200", description = "템플릿 검색 성공") + @ApiErrorResponse(status = HttpStatus.BAD_REQUEST, + instance = "/templates?memberId=1&keyword=\"java\"&tagIds=", errorCases = { + @ErrorCase(description = "태그 ID가 0개인 경우", exampleMessage = "태그 ID가 0개입니다. 필터링 하지 않을 경우 null로 전달해주세요."), + @ErrorCase(description = "페이지 번호가 1보다 작을 경우", exampleMessage = "페이지 번호는 1 이상이어야 합니다."), + }) + @ApiErrorResponse(status = HttpStatus.UNAUTHORIZED, + instance = "/templates?memberId=1&keyword=\"java\"", errorCases = { + @ErrorCase(description = "인증 정보가 없거나 잘못된 경우", exampleMessage = "인증에 실패했습니다."), + @ErrorCase(description = "인증 정보와 멤버 ID가 다른 경우", exampleMessage = "인증 정보에 포함된 멤버 ID와 파라미터로 받은 멤버 ID가 다릅니다."), + }) + @ApiErrorResponse(status = HttpStatus.NOT_FOUND, + instance = "/templates?memberId=1&keyword=\"java\"&categoryId=1&tagIds=1,2", errorCases = { + @ErrorCase(description = "인증 정보에 포함된 멤버가 없는 경우", exampleMessage = "식별자 1에 해당하는 멤버가 존재하지 않습니다."), + @ErrorCase(description = "카테고리가 없는 경우", exampleMessage = "식별자 1에 해당하는 카테고리가 존재하지 않습니다."), + @ErrorCase(description = "태그가 없는 경우", exampleMessage = "식별자 1에 해당하는 태그가 존재하지 않습니다."), + }) ResponseEntity getTemplates( MemberDto memberDto, Long memberId, @@ -72,31 +100,57 @@ ResponseEntity getTemplates( @SecurityRequirement(name = "쿠키 인증 토큰") @Operation(summary = "템플릿 단건 조회", description = "해당하는 식별자의 템플릿을 조회합니다.") - @ApiResponse(responseCode = "200", description = "템플릿 단건 조회 성공", - content = {@Content(schema = @Schema(implementation = ExploreTemplatesResponse.class))}) + @ApiResponse(responseCode = "200", description = "템플릿 단건 조회 성공") @ApiErrorResponse(status = HttpStatus.BAD_REQUEST, instance = "/templates/1", errorCases = { - @ErrorCase(description = "해당하는 id 값인 템플릿이 없는 경우", exampleMessage = "식별자 1에 해당하는 템플릿이 존재하지 않습니다."), - @ErrorCase(description = "템플릿을 가져올 권한이 없는 경우", exampleMessage = "해당 템플릿에 대한 권한이 없는 유저입니다."), + @ErrorCase(description = "해당하는 ID 값인 템플릿이 없는 경우", exampleMessage = "식별자 1에 해당하는 템플릿이 존재하지 않습니다."), + }) + @ApiErrorResponse(status = HttpStatus.UNAUTHORIZED, instance = "/templates/1", errorCases = { + @ErrorCase(description = "인증 정보가 없거나 잘못된 경우", exampleMessage = "인증에 실패했습니다."), + @ErrorCase(description = "자신의 템플릿이 아닐 경우", exampleMessage = "해당 템플릿에 대한 권한이 없습니다."), }) - ResponseEntity getTemplateById(Long id, MemberDto memberDto); + @ApiErrorResponse(status = HttpStatus.NOT_FOUND, instance = "/templates/1", errorCases = { + @ErrorCase(description = "인증 정보에 포함된 멤버가 없는 경우", exampleMessage = "식별자 1에 해당하는 멤버가 존재하지 않습니다."), + }) + ResponseEntity getTemplateById(MemberDto memberDto, Long id); @SecurityRequirement(name = "쿠키 인증 토큰") @Operation(summary = "템플릿 수정", description = "해당하는 식별자의 템플릿을 수정합니다.") @ApiResponse(responseCode = "200", description = "템플릿 수정 성공") @ApiErrorResponse(status = HttpStatus.BAD_REQUEST, instance = "/templates/1", errorCases = { - @ErrorCase(description = "해당하는 id 값인 템플릿이 없는 경우", exampleMessage = "식별자 1에 해당하는 템플릿이 존재하지 않습니다."), - @ErrorCase(description = "템플릿을 수정할 권한이 없는 경우", exampleMessage = "해당 템플릿에 대한 권한이 없는 유저입니다."), - + @ErrorCase(description = "모든 필드 중 null인 값이 있는 경우", exampleMessage = "템플릿 설명이 null 입니다."), + @ErrorCase(description = "템플릿명, 스니펫 파일명, 스니펫 소스 코드가 공백일 경우", exampleMessage = "템플릿명이 비어 있거나 공백입니다."), + @ErrorCase(description = "템플릿명, 스니펫 파일명, 태그명이 255자를 초과한 경우", exampleMessage = "템플릿명은 최대 255자까지 입력 가능합니다."), + @ErrorCase(description = "스니펫 소스 코드가 65,535 byte를 초과한 경우", exampleMessage = "소스 코드는 최대 65,535 Byte까지 입력 " + + "가능합니다."), + @ErrorCase(description = "스니펫 순서가 잘못된 경우", exampleMessage = "스니펫 순서가 잘못되었습니다."), + @ErrorCase(description = "해당 템플릿의 실제 스니펫 수와 인자로 받은 스니펫 수가 다를 경우", exampleMessage = "스니펫의 정보가 정확하지 않습니다."), + }) + @ApiErrorResponse(status = HttpStatus.UNAUTHORIZED, instance = "/templates/1", errorCases = { + @ErrorCase(description = "인증 정보가 없거나 잘못된 경우", exampleMessage = "인증에 실패했습니다."), + @ErrorCase(description = "자신의 템플릿이 아닐 경우", exampleMessage = "해당 템플릿에 대한 권한이 없습니다."), + @ErrorCase(description = "카테고리 권한이 없는 경우", exampleMessage = "해당 카테고리에 대한 권한이 없습니다."), }) - ResponseEntity updateTemplate(Long id, UpdateTemplateRequest updateTemplateRequest, MemberDto memberDto); + @ApiErrorResponse(status = HttpStatus.NOT_FOUND, instance = "/templates/1", errorCases = { + @ErrorCase(description = "인증 정보에 포함된 멤버가 없는 경우", exampleMessage = "식별자 1에 해당하는 멤버가 존재하지 않습니다."), + @ErrorCase(description = "카테고리가 없는 경우", exampleMessage = "식별자 1에 해당하는 카테고리가 존재하지 않습니다."), + @ErrorCase(description = "태그가 없는 경우", exampleMessage = "식별자 1에 해당하는 태그가 존재하지 않습니다."), + @ErrorCase(description = "스니펫이 없는 경우", exampleMessage = "식별자 1에 해당하는 스니펫이 존재하지 않습니다."), + }) + ResponseEntity updateTemplate(MemberDto memberDto, Long id, UpdateTemplateRequest updateTemplateRequest); @SecurityRequirement(name = "쿠키 인증 토큰") - @Operation(summary = "템플릿 삭제", description = "해당하는 식별자의 템플릿을 삭제합니다.") + @Operation(summary = "템플릿 삭제", description = "해당하는 식별자의 템플릿들을 삭제합니다.") @ApiResponse(responseCode = "204", description = "템플릿 삭제 성공") - @ApiErrorResponse(status = HttpStatus.BAD_REQUEST, instance = "/templates/1", errorCases = { - @ErrorCase(description = "해당하는 id 값인 템플릿이 없는 경우", exampleMessage = "식별자 1에 해당하는 템플릿이 존재하지 않습니다."), - @ErrorCase(description = "템플릿을 수정할 권한이 없는 경우", exampleMessage = "해당 템플릿에 대한 권한이 없는 유저입니다."), - + @ApiErrorResponse(status = HttpStatus.BAD_REQUEST, instance = "/templates/1,1", errorCases = { + @ErrorCase(description = "템플릿 ID가 중복된 경우", exampleMessage = "삭제하고자 하는 템플릿 ID가 중복되었습니다."), + }) + @ApiErrorResponse(status = HttpStatus.UNAUTHORIZED, instance = "/templates/1", errorCases = { + @ErrorCase(description = "인증 정보가 없거나 잘못된 경우", exampleMessage = "인증에 실패했습니다."), + @ErrorCase(description = "자신의 템플릿이 아닐 경우", exampleMessage = "해당 템플릿에 대한 권한이 없습니다."), + }) + @ApiErrorResponse(status = HttpStatus.NOT_FOUND, instance = "/templates/1", errorCases = { + @ErrorCase(description = "인증 정보에 포함된 멤버가 없는 경우", exampleMessage = "식별자 1에 해당하는 멤버가 존재하지 않습니다."), + @ErrorCase(description = "템플릿이 없는 경우", exampleMessage = "식별자 1에 해당하는 템플릿이 존재하지 않습니다."), }) - ResponseEntity deleteTemplate(Long id, MemberDto memberDto); + ResponseEntity deleteTemplates(MemberDto memberDto, List ids); } diff --git a/backend/src/main/java/codezap/template/controller/TemplateController.java b/backend/src/main/java/codezap/template/controller/TemplateController.java index aa69eec4d..e101c1035 100644 --- a/backend/src/main/java/codezap/template/controller/TemplateController.java +++ b/backend/src/main/java/codezap/template/controller/TemplateController.java @@ -36,11 +36,11 @@ public class TemplateController implements SpringDocTemplateController { private final TemplateService templateService; @PostMapping - public ResponseEntity create( - @Validated(ValidationSequence.class) @RequestBody CreateTemplateRequest createTemplateRequest, - @BasicAuthentication MemberDto memberDto + public ResponseEntity createTemplate( + @BasicAuthentication MemberDto memberDto, + @Validated(ValidationSequence.class) @RequestBody CreateTemplateRequest createTemplateRequest ) { - Long createdTemplateId = templateService.createTemplate(createTemplateRequest, memberDto); + Long createdTemplateId = templateService.createTemplate(memberDto, createTemplateRequest); return ResponseEntity.created(URI.create("/templates/" + createdTemplateId)) .build(); } @@ -59,6 +59,14 @@ public ResponseEntity getTemplates( return ResponseEntity.ok(response); } + @GetMapping("/{id}") + public ResponseEntity getTemplateById( + @BasicAuthentication MemberDto memberDto, + @PathVariable Long id + ) { + return ResponseEntity.ok(templateService.findByIdAndMember(id, memberDto)); + } + @GetMapping("/tags") public ResponseEntity getTags( @BasicAuthentication MemberDto memberDto, @@ -73,26 +81,22 @@ public ResponseEntity explore() { return ResponseEntity.ok(templateService.findAll()); } - @GetMapping("/{id}") - public ResponseEntity getTemplateById(@PathVariable Long id, - @BasicAuthentication MemberDto memberDto - ) { - return ResponseEntity.ok(templateService.findByIdAndMember(id, memberDto)); - } - @PostMapping("/{id}") public ResponseEntity updateTemplate( + @BasicAuthentication MemberDto memberDto, @PathVariable Long id, - @Validated(ValidationSequence.class) @RequestBody UpdateTemplateRequest updateTemplateRequest, - @BasicAuthentication MemberDto memberDto + @Validated(ValidationSequence.class) @RequestBody UpdateTemplateRequest updateTemplateRequest ) { - templateService.update(id, updateTemplateRequest, memberDto); + templateService.update(memberDto, id, updateTemplateRequest); return ResponseEntity.ok().build(); } - @DeleteMapping("/{id}") - public ResponseEntity deleteTemplate(@PathVariable Long id, @BasicAuthentication MemberDto memberDto) { - templateService.deleteById(id, memberDto); + @DeleteMapping("/{ids}") + public ResponseEntity deleteTemplates( + @BasicAuthentication MemberDto memberDto, + @PathVariable List ids + ) { + templateService.deleteByIds(memberDto, ids); return ResponseEntity.noContent().build(); } } diff --git a/backend/src/main/java/codezap/template/dto/request/CreateSnippetRequest.java b/backend/src/main/java/codezap/template/dto/request/CreateSnippetRequest.java index cc527635d..fb2f2c671 100644 --- a/backend/src/main/java/codezap/template/dto/request/CreateSnippetRequest.java +++ b/backend/src/main/java/codezap/template/dto/request/CreateSnippetRequest.java @@ -10,14 +10,14 @@ import io.swagger.v3.oas.annotations.media.Schema; public record CreateSnippetRequest( - @Schema(description = "파일 이름", example = "Main.java") - @NotBlank(message = "파일 이름이 null 입니다.", groups = NotNullGroup.class) - @Size(max = 255, message = "파일 이름은 최대 255자까지 입력 가능합니다.", groups = SizeCheckGroup.class) + @Schema(description = "파일명", example = "Main.java") + @NotBlank(message = "파일명이 비어 있거나 공백입니다.", groups = NotNullGroup.class) + @Size(max = 255, message = "파일명은 최대 255자까지 입력 가능합니다.", groups = SizeCheckGroup.class) String filename, @Schema(description = "소스 코드", example = "public class Main { // ...") - @NotBlank(message = "파일 내용이 null 입니다.", groups = NotNullGroup.class) - @ByteLength(max = 65_535, message = "파일 내용은 최대 65,535 Byte까지 입력 가능합니다.", groups = SizeCheckGroup.class) + @NotBlank(message = "소스 코드가 비어 있거나 공백입니다.", groups = NotNullGroup.class) + @ByteLength(max = 65_535, message = "소스 코드는 최대 65,535 Byte까지 입력 가능합니다.", groups = SizeCheckGroup.class) String content, @Schema(description = "스니펫 순서", example = "1") diff --git a/backend/src/main/java/codezap/template/dto/request/CreateTemplateRequest.java b/backend/src/main/java/codezap/template/dto/request/CreateTemplateRequest.java index dac09639e..76bc97e5f 100644 --- a/backend/src/main/java/codezap/template/dto/request/CreateTemplateRequest.java +++ b/backend/src/main/java/codezap/template/dto/request/CreateTemplateRequest.java @@ -14,9 +14,9 @@ import io.swagger.v3.oas.annotations.media.Schema; public record CreateTemplateRequest( - @Schema(description = "템플릿 이름", example = "스프링 로그인 구현") - @NotBlank(message = "템플릿 이름이 null 입니다.", groups = NotNullGroup.class) - @Size(max = 255, message = "템플릿 이름은 최대 255자까지 입력 가능합니다.", groups = SizeCheckGroup.class) + @Schema(description = "템플릿명", example = "스프링 로그인 구현") + @NotBlank(message = "템플릿명이 비어 있거나 공백입니다.", groups = NotNullGroup.class) + @Size(max = 255, message = "템플릿명은 최대 255자까지 입력 가능합니다.", groups = SizeCheckGroup.class) String title, @Schema(description = "템플릿 설명", example = "JWT를 사용하여 로그인 기능을 구현함") @@ -24,18 +24,22 @@ public record CreateTemplateRequest( @ByteLength(max = 65_535, message = "템플릿 설명은 최대 65,535 Byte까지 입력 가능합니다.", groups = SizeCheckGroup.class) String description, - @Schema(description = "템플릿의 스니펫 내역") - @NotNull(message = "스니펫 리스트가 null 입니다.", groups = NotNullGroup.class) + @Schema(description = "스니펫 목록") + @NotNull(message = "스니펫 목록이 null 입니다.", groups = NotNullGroup.class) @Size(min = 1, message = "스니펫은 최소 1개 입력해야 합니다.", groups = SizeCheckGroup.class) @Valid List snippets, + @Schema(description = "썸네일 스니펫 순서", example = "1") + @NotNull(message = "썸네일 스니펫 순서가 null 입니다.") + int thumbnailOrdinal, + @Schema(description = "카테고리 ID", example = "1") - @NotNull(message = "카테고리 id가 null 입니다.") + @NotNull(message = "카테고리 ID가 null 입니다.") Long categoryId, - @Schema(description = "태그 리스트") - @NotNull(message = "태그 리스트가 null 입니다.") + @Schema(description = "태그 목록") + @NotNull(message = "태그 목록이 null 입니다.") List tags ) implements ValidatedSnippetsOrdinalRequest { @Override diff --git a/backend/src/main/java/codezap/template/dto/request/UpdateSnippetRequest.java b/backend/src/main/java/codezap/template/dto/request/UpdateSnippetRequest.java index bce59a6d8..84dca114c 100644 --- a/backend/src/main/java/codezap/template/dto/request/UpdateSnippetRequest.java +++ b/backend/src/main/java/codezap/template/dto/request/UpdateSnippetRequest.java @@ -10,18 +10,18 @@ import io.swagger.v3.oas.annotations.media.Schema; public record UpdateSnippetRequest( - @Schema(description = "파일 식별자", example = "0") - @NotNull(message = "파일 id가 null 입니다.") + @Schema(description = "스니펫 ID", example = "0") + @NotNull(message = "스니펫 ID가 null 입니다.") Long id, - @Schema(description = "파일 이름", example = "Main.java") - @NotBlank(message = "파일 이름이 null 입니다.", groups = NotNullGroup.class) - @Size(max = 255, message = "파일 이름은 최대 255자까지 입력 가능합니다.", groups = SizeCheckGroup.class) + @Schema(description = "파일명", example = "Main.java") + @NotBlank(message = "파일명이 비어 있거나 공백입니다.", groups = NotNullGroup.class) + @Size(max = 255, message = "파일명은 최대 255자까지 입력 가능합니다.", groups = SizeCheckGroup.class) String filename, @Schema(description = "소스 코드", example = "public class Main { // ...") - @NotBlank(message = "파일 내용이 null 입니다.", groups = NotNullGroup.class) - @ByteLength(max = 65_535, message = "파일 내용은 최대 65,535 Byte까지 입력 가능합니다.", groups = SizeCheckGroup.class) + @NotBlank(message = "소스 코드가 비어 있거나 공백입니다.", groups = NotNullGroup.class) + @ByteLength(max = 65_535, message = "소스 코드는 최대 65,535 Byte까지 입력 가능합니다.", groups = SizeCheckGroup.class) String content, @Schema(description = "스니펫 순서", example = "1") diff --git a/backend/src/main/java/codezap/template/dto/request/UpdateTemplateRequest.java b/backend/src/main/java/codezap/template/dto/request/UpdateTemplateRequest.java index e99232cfe..ab0f932fa 100644 --- a/backend/src/main/java/codezap/template/dto/request/UpdateTemplateRequest.java +++ b/backend/src/main/java/codezap/template/dto/request/UpdateTemplateRequest.java @@ -16,9 +16,9 @@ import io.swagger.v3.oas.annotations.media.Schema; public record UpdateTemplateRequest( - @Schema(description = "템플릿 이름", example = "스프링 로그인 구현") - @NotBlank(message = "템플릿 이름이 null 입니다.", groups = NotNullGroup.class) - @Size(max = 255, message = "템플릿 이름은 최대 255자까지 입력 가능합니다.", groups = SizeCheckGroup.class) + @Schema(description = "템플릿명", example = "스프링 로그인 구현") + @NotBlank(message = "템플릿명이 비어 있거나 공백입니다.", groups = NotNullGroup.class) + @Size(max = 255, message = "템플릿명은 최대 255자까지 입력 가능합니다.", groups = SizeCheckGroup.class) String title, @Schema(description = "템플릿 설명", example = "JWT를 사용하여 로그인 기능을 구현함") @@ -26,26 +26,26 @@ public record UpdateTemplateRequest( @ByteLength(max = 65_535, message = "템플릿 설명은 최대 65,535 Byte까지 입력 가능합니다.", groups = SizeCheckGroup.class) String description, - @Schema(description = "새로 추가한 스니펫 내역") - @NotNull(message = "createSnippets 리스트가 null 입니다.", groups = NotNullGroup.class) + @Schema(description = "추가하는 스니펫 목록") + @NotNull(message = "추가하는 스니펫 목록이 null 입니다.", groups = NotNullGroup.class) @Valid List createSnippets, - @Schema(description = "삭제, 생성 스니펫을 제외한 모든 스니펫 내역") - @NotNull(message = "updateSnippets 리스트가 null 입니다.", groups = NotNullGroup.class) + @Schema(description = "삭제, 생성 스니펫을 제외한 모든 스니펫 목록") + @NotNull(message = "삭제, 생성 스니펫을 제외한 모든 스니펫 목록이 null 입니다.", groups = NotNullGroup.class) @Valid List updateSnippets, - @Schema(description = "삭제한 스니펫 식별자") - @NotNull(message = "deleteSnippetIds 리스트가 null 입니다.") + @Schema(description = "삭제하는 스니펫 ID 목록") + @NotNull(message = "삭제하는 스니펫 ID 목록이 null 입니다.") List deleteSnippetIds, @Schema(description = "카테고리 ID", example = "1") - @NotNull(message = "카테고리 id가 null 입니다.") + @NotNull(message = "카테고리 ID가 null 입니다.") Long categoryId, - @Schema(description = "태그 리스트") - @NotNull(message = "태그 리스트가 null 입니다.") + @Schema(description = "태그 목록") + @NotNull(message = "태그 목록이 null 입니다.") List tags ) implements ValidatedSnippetsOrdinalRequest, ValidatedSnippetsCountRequest { @Override diff --git a/backend/src/main/java/codezap/template/dto/response/FindAllTemplatesResponse.java b/backend/src/main/java/codezap/template/dto/response/FindAllTemplatesResponse.java index 01eab1b78..7f32ef494 100644 --- a/backend/src/main/java/codezap/template/dto/response/FindAllTemplatesResponse.java +++ b/backend/src/main/java/codezap/template/dto/response/FindAllTemplatesResponse.java @@ -16,14 +16,16 @@ public record FindAllTemplatesResponse( List templates ) { public record ItemResponse( - @Schema(description = "템플릿 식별자", example = "0") + @Schema(description = "템플릿 ID", example = "0") Long id, - @Schema(description = "템플릿 이름", example = "스프링 로그인 구현") + @Schema(description = "템플릿명", example = "스프링 로그인 구현") String title, @Schema(description = "템플릿 설명", example = "Jwt 토큰을 이용하여 로그인 기능을 구현합니다.") String description, @Schema(description = "태그 목록") List tags, + @Schema(description = "썸네일 스니펫") + FindThumbnailSnippetResponse thumbnailSnippet, @Schema(description = "템플릿 생성 시간", example = "2024-11-10 12:00", type = "string") LocalDateTime createdAt, @Schema(description = "템플릿 수정 시간", example = "2024-11-11 12:00", type = "string") @@ -37,6 +39,7 @@ public static ItemResponse of(Template template, List templateTags) { templateTags.stream() .map(tag -> new FindTagResponse(tag.getId(), tag.getName())) .toList(), + null, template.getCreatedAt(), template.getModifiedAt() ); diff --git a/backend/src/main/java/codezap/template/dto/response/FindThumbnailSnippetResponse.java b/backend/src/main/java/codezap/template/dto/response/FindThumbnailSnippetResponse.java index de47fbe4f..bfd7726d2 100644 --- a/backend/src/main/java/codezap/template/dto/response/FindThumbnailSnippetResponse.java +++ b/backend/src/main/java/codezap/template/dto/response/FindThumbnailSnippetResponse.java @@ -4,11 +4,11 @@ import io.swagger.v3.oas.annotations.media.Schema; public record FindThumbnailSnippetResponse( - @Schema(description = "파일 이름", example = "Main.java") + @Schema(description = "썸네일 파일명", example = "Main.java") String filename, - @Schema(description = "목록 조회 시 보여질 코드", example = "public class Main { // ...") - String thumbnailContent + @Schema(description = "썸네일 소스 코드", example = "public class Main { // ...") + String content ) { public static FindThumbnailSnippetResponse from(Snippet snippet) { return new FindThumbnailSnippetResponse( diff --git a/backend/src/main/java/codezap/template/service/TemplateService.java b/backend/src/main/java/codezap/template/service/TemplateService.java index 812d2ed5c..ee5321234 100644 --- a/backend/src/main/java/codezap/template/service/TemplateService.java +++ b/backend/src/main/java/codezap/template/service/TemplateService.java @@ -53,7 +53,7 @@ public class TemplateService { private final MemberRepository memberRepository; @Transactional - public Long createTemplate(CreateTemplateRequest createTemplateRequest, MemberDto memberDto) { + public Long createTemplate(MemberDto memberDto, CreateTemplateRequest createTemplateRequest) { Member member = memberRepository.fetchById(memberDto.id()); Category category = categoryRepository.fetchById(createTemplateRequest.categoryId()); validateCategoryAuthorizeMember(category, member); @@ -75,7 +75,7 @@ public Long createTemplate(CreateTemplateRequest createTemplateRequest, MemberDt private void validateCategoryAuthorizeMember(Category category, Member member) { if (!category.getMember().equals(member)) { - throw new CodeZapException(HttpStatus.UNAUTHORIZED, "해당 카테고리에 대한 권한이 없는 유저입니다."); + throw new CodeZapException(HttpStatus.UNAUTHORIZED, "해당 카테고리에 대한 권한이 없습니다."); } } @@ -121,7 +121,7 @@ public FindTemplateResponse findByIdAndMember(Long id, MemberDto memberDto) { private void validateTemplateAuthorizeMember(Template template, Member member) { if (!template.getMember().equals(member)) { - throw new CodeZapException(HttpStatus.UNAUTHORIZED, "해당 템플릿에 대한 권한이 없는 유저입니다."); + throw new CodeZapException(HttpStatus.UNAUTHORIZED, "해당 템플릿에 대한 권한이 없습니다."); } } @@ -168,7 +168,7 @@ private List getTemplateTags(Template template) { } @Transactional - public void update(Long templateId, UpdateTemplateRequest updateTemplateRequest, MemberDto memberDto) { + public void update(MemberDto memberDto, Long templateId, UpdateTemplateRequest updateTemplateRequest) { Member member = memberRepository.fetchById(memberDto.id()); Category category = categoryRepository.fetchById(updateTemplateRequest.categoryId()); validateCategoryAuthorizeMember(category, member); @@ -245,7 +245,13 @@ private void validateSnippetsCount(UpdateTemplateRequest updateTemplateRequest, } @Transactional - public void deleteById(Long id, MemberDto memberDto) { + public void deleteByIds(MemberDto memberDto, List ids) { + for (Long id : ids) { + deleteById(memberDto, id); + } + } + + private void deleteById(MemberDto memberDto, Long id) { Member member = memberRepository.fetchById(memberDto.id()); Template template = templateRepository.fetchById(id); validateTemplateAuthorizeMember(template, member); diff --git a/backend/src/test/java/codezap/category/controller/CategoryControllerTest.java b/backend/src/test/java/codezap/category/controller/CategoryControllerTest.java index a9f87baf9..2925c0a1b 100644 --- a/backend/src/test/java/codezap/category/controller/CategoryControllerTest.java +++ b/backend/src/test/java/codezap/category/controller/CategoryControllerTest.java @@ -285,14 +285,15 @@ void updateCategoryFailWithDuplicatedName() throws Exception { void updateCategoryFailWithLongName() throws Exception { // given templateService.createTemplate( + MemberDtoFixture.getFirstMemberDto(), new CreateTemplateRequest( "title", "description", List.of(new CreateSnippetRequest("filename", "content", 1)), + 1, savedCategoryId, List.of("tag1", "tag2") - ), - MemberDtoFixture.getFirstMemberDto() + ) ); // when & then diff --git a/backend/src/test/java/codezap/template/controller/TemplateControllerTest.java b/backend/src/test/java/codezap/template/controller/TemplateControllerTest.java index 24c00e8c2..78fecd771 100644 --- a/backend/src/test/java/codezap/template/controller/TemplateControllerTest.java +++ b/backend/src/test/java/codezap/template/controller/TemplateControllerTest.java @@ -111,6 +111,7 @@ void createTemplateSuccess(String repeatTarget, int maxLength) throws Exception maxTitle, repeatTarget.repeat(maxLength), List.of(new CreateSnippetRequest("a".repeat(MAX_LENGTH), repeatTarget.repeat(maxLength), 1)), + 1, 1L, List.of("tag1", "tag2") ); @@ -131,6 +132,7 @@ void createTemplateFailWithNotLogin() throws Exception { maxTitle, "description", List.of(new CreateSnippetRequest("filename", "content", 1)), + 1, 1L, List.of("tag1", "tag2") ); @@ -150,6 +152,7 @@ void createTemplateFailWithLongTitle() throws Exception { exceededTitle, "description", List.of(new CreateSnippetRequest("a", "content", 1)), + 1, 1L, List.of("tag1", "tag2") ); @@ -160,7 +163,7 @@ void createTemplateFailWithLongTitle() throws Exception { .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(templateRequest))) .andExpect(status().isBadRequest()) - .andExpect(jsonPath("$.detail").value("템플릿 이름은 최대 255자까지 입력 가능합니다.")); + .andExpect(jsonPath("$.detail").value("템플릿명은 최대 255자까지 입력 가능합니다.")); } @Test @@ -171,6 +174,7 @@ void createTemplateFailWithLongFileName() throws Exception { "title", "description", List.of(new CreateSnippetRequest(exceededTitle, "content", 1)), + 1, 1L, List.of("tag1", "tag2") ); @@ -181,7 +185,7 @@ void createTemplateFailWithLongFileName() throws Exception { .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(templateRequest))) .andExpect(status().isBadRequest()) - .andExpect(jsonPath("$.detail").value("파일 이름은 최대 255자까지 입력 가능합니다.")); + .andExpect(jsonPath("$.detail").value("파일명은 최대 255자까지 입력 가능합니다.")); } @ParameterizedTest @@ -192,6 +196,7 @@ void createTemplateFailWithLongContent(String repeatTarget, int exceededLength) "title", "description", List.of(new CreateSnippetRequest("title", repeatTarget.repeat(exceededLength), 1)), + 1, 1L, List.of("tag1", "tag2") ); @@ -202,7 +207,7 @@ void createTemplateFailWithLongContent(String repeatTarget, int exceededLength) .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(templateRequest))) .andExpect(status().isBadRequest()) - .andExpect(jsonPath("$.detail").value("파일 내용은 최대 65,535 Byte까지 입력 가능합니다.")); + .andExpect(jsonPath("$.detail").value("소스 코드는 최대 65,535 Byte까지 입력 가능합니다.")); } @ParameterizedTest @@ -213,6 +218,7 @@ void createTemplateFailWithLongDescription(String repeatTarget, int exceededLeng "title", repeatTarget.repeat(exceededLength), List.of(new CreateSnippetRequest("title", "content", 1)), + 1, 1L, List.of("tag1", "tag2") ); @@ -230,12 +236,12 @@ void createTemplateFailWithLongDescription(String repeatTarget, int exceededLeng @DisplayName("템플릿 생성 실패: 잘못된 스니펫 순서 입력") @CsvSource({"0, 1", "1, 3", "2, 1"}) void createTemplateFailWithWrongSnippetOrdinal(int firstIndex, int secondIndex) throws Exception { - MemberDto memberDto = MemberDtoFixture.getFirstMemberDto(); CreateTemplateRequest templateRequest = new CreateTemplateRequest( "title", "description", List.of(new CreateSnippetRequest("title", "content", firstIndex), new CreateSnippetRequest("title", "content", secondIndex)), + 1, 1L, List.of("tag1", "tag2") ); @@ -257,8 +263,8 @@ void findAllTemplatesSuccess() throws Exception { MemberDto memberDto = MemberDtoFixture.getFirstMemberDto(); CreateTemplateRequest templateRequest1 = createTemplateRequestWithTwoSnippets("title1"); CreateTemplateRequest templateRequest2 = createTemplateRequestWithTwoSnippets("title2"); - templateService.createTemplate(templateRequest1, memberDto); - templateService.createTemplate(templateRequest2, memberDto); + templateService.createTemplate(memberDto, templateRequest1); + templateService.createTemplate(memberDto, templateRequest2); // when & then mvc.perform(get("/templates") @@ -282,7 +288,7 @@ void findOneTemplateSuccess() throws Exception { // given MemberDto memberDto = MemberDtoFixture.getFirstMemberDto(); CreateTemplateRequest templateRequest = createTemplateRequestWithTwoSnippets("title"); - templateService.createTemplate(templateRequest, memberDto); + templateService.createTemplate(memberDto, templateRequest); // when & then mvc.perform(get("/templates/1") @@ -315,7 +321,7 @@ void findOneTemplateFailWithUnauthorized() throws Exception { // given MemberDto memberDto = MemberDtoFixture.getFirstMemberDto(); CreateTemplateRequest templateRequest = createTemplateRequestWithTwoSnippets("title"); - templateService.createTemplate(templateRequest, memberDto); + templateService.createTemplate(memberDto, templateRequest); // when String basicAuth = HttpHeaders.encodeBasicAuth(secondMember.getEmail(), secondMember.getPassword(), StandardCharsets.UTF_8); @@ -327,7 +333,7 @@ void findOneTemplateFailWithUnauthorized() throws Exception { .accept(MediaType.APPLICATION_JSON) .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isUnauthorized()) - .andExpect(jsonPath("$.detail").value("해당 템플릿에 대한 권한이 없는 유저입니다.")); + .andExpect(jsonPath("$.detail").value("해당 템플릿에 대한 권한이 없습니다.")); } } @@ -395,7 +401,7 @@ void updateTemplateFailWithUnauthorized() throws Exception { .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(updateTemplateRequest))) .andExpect(status().isUnauthorized()) - .andExpect(jsonPath("$.detail").value("해당 템플릿에 대한 권한이 없는 유저입니다.")); + .andExpect(jsonPath("$.detail").value("해당 템플릿에 대한 권한이 없습니다.")); } @Test @@ -426,7 +432,7 @@ void updateTemplateFailWithLongName() throws Exception { .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(updateTemplateRequest))) .andExpect(status().isBadRequest()) - .andExpect(jsonPath("$.detail").value("템플릿 이름은 최대 255자까지 입력 가능합니다.")); + .andExpect(jsonPath("$.detail").value("템플릿명은 최대 255자까지 입력 가능합니다.")); } @Test @@ -457,7 +463,7 @@ void updateTemplateFailWithLongFileName() throws Exception { .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(updateTemplateRequest))) .andExpect(status().isBadRequest()) - .andExpect(jsonPath("$.detail").value("파일 이름은 최대 255자까지 입력 가능합니다.")); + .andExpect(jsonPath("$.detail").value("파일명은 최대 255자까지 입력 가능합니다.")); } @ParameterizedTest @@ -488,7 +494,7 @@ void updateTemplateFailWithLongFileContent(String repeatTarget, int exceedLength .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(updateTemplateRequest))) .andExpect(status().isBadRequest()) - .andExpect(jsonPath("$.detail").value("파일 내용은 최대 65,535 Byte까지 입력 가능합니다.")); + .andExpect(jsonPath("$.detail").value("소스 코드는 최대 65,535 Byte까지 입력 가능합니다.")); } @ParameterizedTest @@ -560,7 +566,7 @@ private void createTemplateAndTwoCategories() { categoryService.create(new CreateCategoryRequest("category1"), memberDto); categoryService.create(new CreateCategoryRequest("category2"), memberDto); CreateTemplateRequest templateRequest = createTemplateRequestWithTwoSnippets("title"); - templateService.createTemplate(templateRequest, memberDto); + templateService.createTemplate(memberDto, templateRequest); } } @@ -575,7 +581,7 @@ void deleteTemplateSuccess() throws Exception { MemberDto memberDto = MemberDtoFixture.getFirstMemberDto(); categoryService.create(new CreateCategoryRequest("category"), memberDto); CreateTemplateRequest templateRequest = createTemplateRequestWithTwoSnippets("title"); - templateService.createTemplate(templateRequest, memberDto); + templateService.createTemplate(memberDto, templateRequest); // when & then mvc.perform(delete("/templates/1") @@ -592,7 +598,7 @@ void deleteTemplateFailWithUnauthorized() throws Exception { MemberDto memberDto = MemberDtoFixture.getFirstMemberDto(); categoryService.create(new CreateCategoryRequest("category"), memberDto); CreateTemplateRequest templateRequest = createTemplateRequestWithTwoSnippets("title"); - templateService.createTemplate(templateRequest, memberDto); + templateService.createTemplate(memberDto, templateRequest); // when String basicAuth = HttpHeaders.encodeBasicAuth(secondMember.getEmail(), secondMember.getPassword(), StandardCharsets.UTF_8); @@ -604,7 +610,7 @@ void deleteTemplateFailWithUnauthorized() throws Exception { .accept(MediaType.APPLICATION_JSON) .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isUnauthorized()) - .andExpect(jsonPath("$.detail").value("해당 템플릿에 대한 권한이 없는 유저입니다.")); + .andExpect(jsonPath("$.detail").value("해당 템플릿에 대한 권한이 없습니다.")); } @Test @@ -627,6 +633,7 @@ private static CreateTemplateRequest createTemplateRequestWithTwoSnippets(String new CreateSnippetRequest("filename1", "content1", 1), new CreateSnippetRequest("filename2", "content2", 2) ), + 1, 1L, List.of("tag1", "tag2") ); diff --git a/backend/src/test/java/codezap/template/service/TagSearchTest.java b/backend/src/test/java/codezap/template/service/TagSearchTest.java index a0ac9bb46..16c5a4b52 100644 --- a/backend/src/test/java/codezap/template/service/TagSearchTest.java +++ b/backend/src/test/java/codezap/template/service/TagSearchTest.java @@ -78,6 +78,7 @@ private CreateTemplateRequest makeTemplateRequest(String title) { return new CreateTemplateRequest(title, "description", List.of(new CreateSnippetRequest("filename1", "content1", 1), new CreateSnippetRequest("filename2", "content2", 2)), + 1, 1L, List.of()); } diff --git a/backend/src/test/java/codezap/template/service/TemplateServiceSearchTest.java b/backend/src/test/java/codezap/template/service/TemplateServiceSearchTest.java index f03cf9dc3..38d00cbe1 100644 --- a/backend/src/test/java/codezap/template/service/TemplateServiceSearchTest.java +++ b/backend/src/test/java/codezap/template/service/TemplateServiceSearchTest.java @@ -79,6 +79,7 @@ private CreateTemplateRequest makeTemplateRequest(String title) { return new CreateTemplateRequest(title, "description", List.of(new CreateSnippetRequest("filename1", "content1", 1), new CreateSnippetRequest("filename2", "content2", 2)), + 1, 1L, List.of()); } @@ -187,12 +188,16 @@ void findAllDescriptionContainKeywordSuccess() { Category category = categoryRepository.save(new Category("category", member)); CreateTemplateRequest request1 = new CreateTemplateRequest("타이틀", "Login 구현", List.of(new CreateSnippetRequest("filename1", "content1", 1), - new CreateSnippetRequest("filename2", "content2", 2)), category.getId(), + new CreateSnippetRequest("filename2", "content2", 2)), + 1, + category.getId(), List.of("tag1", "tag2")); saveTemplate(request1, member, category); CreateTemplateRequest request2 = new CreateTemplateRequest("타이틀", "Signup 구현", List.of(new CreateSnippetRequest("filename1", "content1", 1), - new CreateSnippetRequest("filename2", "content2", 2)), category.getId(), + new CreateSnippetRequest("filename2", "content2", 2)), + 1, + category.getId(), List.of("tag1", "tag2")); saveTemplate(request2, member, category); diff --git a/backend/src/test/java/codezap/template/service/TemplateServiceTest.java b/backend/src/test/java/codezap/template/service/TemplateServiceTest.java index e423c0da5..1ac941f6b 100644 --- a/backend/src/test/java/codezap/template/service/TemplateServiceTest.java +++ b/backend/src/test/java/codezap/template/service/TemplateServiceTest.java @@ -9,7 +9,6 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import codezap.CodeZapApplication; import codezap.category.domain.Category; import codezap.category.repository.CategoryRepository; import codezap.category.repository.FakeCategoryRepository; @@ -51,7 +50,8 @@ class TemplateServiceTest { private final TemplateRepository templateRepository = new FakeTemplateRepository(); private final SnippetRepository snippetRepository = new FakeSnippetRepository(); private final ThumbnailSnippetRepository thumbnailSnippetRepository = new FakeThumbnailSnippetRepository(); - private final CategoryRepository categoryRepository = new FakeCategoryRepository(List.of(firstCategory, secondCategory)); + private final CategoryRepository categoryRepository = new FakeCategoryRepository( + List.of(firstCategory, secondCategory)); private final TemplateTagRepository templateTagRepository = new FakeTemplateTagRepository(); private final TagRepository tagRepository = new FakeTagRepository(); private final MemberRepository memberRepository = new FakeMemberRepository(List.of(firstMember, secondMember)); @@ -72,7 +72,7 @@ void createTemplateSuccess() { CreateTemplateRequest createTemplateRequest = makeTemplateRequest("title"); // when - Long id = templateService.createTemplate(createTemplateRequest, memberDto); + Long id = templateService.createTemplate(memberDto, createTemplateRequest); Template template = templateRepository.fetchById(id); // then @@ -136,7 +136,7 @@ void findOneTemplateFailWithUnauthorized() { // then assertThatCode(() -> templateService.findByIdAndMember(template.getId(), otherMemberDto)) .isInstanceOf(CodeZapException.class) - .hasMessage("해당 템플릿에 대한 권한이 없는 유저입니다."); + .hasMessage("해당 템플릿에 대한 권한이 없습니다."); } @Test @@ -151,7 +151,7 @@ void updateTemplateSuccess() { // when UpdateTemplateRequest updateTemplateRequest = makeUpdateTemplateRequest("updateTitle", 1L); - templateService.update(template.getId(), updateTemplateRequest, memberDto); + templateService.update(memberDto, template.getId(), updateTemplateRequest); Template updateTemplate = templateRepository.fetchById(template.getId()); List snippets = snippetRepository.findAllByTemplate(template); ThumbnailSnippet thumbnailSnippet = thumbnailSnippetRepository.fetchById(template.getId()); @@ -185,9 +185,9 @@ void updateTemplateFailWithUnauthorized() { UpdateTemplateRequest updateTemplateRequest = makeUpdateTemplateRequest("updateTitle", 2L); // then - assertThatCode(() -> templateService.update(template.getId(), updateTemplateRequest, otherMemberDto)) + assertThatCode(() -> templateService.update(otherMemberDto, template.getId(), updateTemplateRequest)) .isInstanceOf(CodeZapException.class) - .hasMessage("해당 템플릿에 대한 권한이 없는 유저입니다."); + .hasMessage("해당 템플릿에 대한 권한이 없습니다."); } @Test @@ -200,7 +200,7 @@ void deleteTemplateSuccess() { saveTemplate(createdTemplate, new Category("category1", member), member); // when - templateService.deleteById(1L, memberDto); + templateService.deleteByIds(memberDto, List.of(1L)); // then assertAll( @@ -223,9 +223,9 @@ void deleteTemplateFailWithUnauthorized() { MemberDto otherMemberDto = MemberDtoFixture.getSecondMemberDto(); // then - assertThatCode(() -> templateService.deleteById(1L, otherMemberDto)) + assertThatCode(() -> templateService.deleteByIds(otherMemberDto, List.of(1L))) .isInstanceOf(CodeZapException.class) - .hasMessage("해당 템플릿에 대한 권한이 없는 유저입니다."); + .hasMessage("해당 템플릿에 대한 권한이 없습니다."); } private CreateTemplateRequest makeTemplateRequest(String title) { @@ -236,6 +236,7 @@ private CreateTemplateRequest makeTemplateRequest(String title) { new CreateSnippetRequest("filename1", "content1", 1), new CreateSnippetRequest("filename2", "content2", 2) ), + 1, 1L, List.of("tag1", "tag2") ); @@ -278,46 +279,4 @@ private Template saveTemplate(CreateTemplateRequest createTemplateRequest, Categ return savedTemplate; } - - private void saveTemplateBySnippetFilename(String templateTitle, String firstFilename, String secondFilename) { - MemberDto memberDto = MemberDtoFixture.getFirstMemberDto(); - Member member = memberRepository.fetchById(memberDto.id()); - Category category = categoryRepository.save(new Category("category", member)); - CreateTemplateRequest createTemplateRequest = new CreateTemplateRequest( - templateTitle, "설명", - List.of( - new CreateSnippetRequest(firstFilename, "content1", 1), - new CreateSnippetRequest(secondFilename, "content2", 2) - ), - category.getId(), - List.of() - ); - Template savedTemplate = templateRepository.save( - new Template(member, createTemplateRequest.title(), createTemplateRequest.description(), category)); - - Snippet savedFirstSnippet = snippetRepository.save(new Snippet(savedTemplate, firstFilename, "content1", 1)); - snippetRepository.save(new Snippet(savedTemplate, secondFilename, "content2", 2)); - thumbnailSnippetRepository.save(new ThumbnailSnippet(savedTemplate, savedFirstSnippet)); - } - - private void saveTemplateBySnippetContent(String templateTitle, String firstContent, String secondContent) { - MemberDto memberDto = MemberDtoFixture.getFirstMemberDto(); - Member member = memberRepository.fetchById(memberDto.id()); - Category category = categoryRepository.save(new Category("category", member)); - CreateTemplateRequest createTemplateRequest = new CreateTemplateRequest( - templateTitle, "설명", - List.of( - new CreateSnippetRequest("filename1", firstContent, 1), - new CreateSnippetRequest("filename2", secondContent, 2) - ), - category.getId(), - List.of() - ); - Template savedTemplate = templateRepository.save( - new Template(member, createTemplateRequest.title(), createTemplateRequest.description(), category)); - - Snippet savedFirstSnippet = snippetRepository.save(new Snippet(savedTemplate, "filename1", firstContent, 1)); - snippetRepository.save(new Snippet(savedTemplate, "filename2", secondContent, 2)); - thumbnailSnippetRepository.save(new ThumbnailSnippet(savedTemplate, savedFirstSnippet)); - } }