diff --git a/src/main/java/com/api/readinglog/common/swagger/SwaggerConfig.java b/src/main/java/com/api/readinglog/common/swagger/SwaggerConfig.java new file mode 100644 index 0000000..014d687 --- /dev/null +++ b/src/main/java/com/api/readinglog/common/swagger/SwaggerConfig.java @@ -0,0 +1,45 @@ +package com.api.readinglog.common.swagger; + + +import io.swagger.v3.oas.annotations.OpenAPIDefinition; +import io.swagger.v3.oas.annotations.info.Contact; +import io.swagger.v3.oas.annotations.info.Info; +import io.swagger.v3.oas.annotations.servers.Server; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.models.Components; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.security.SecurityRequirement; +import io.swagger.v3.oas.models.security.SecurityScheme; +import java.util.Arrays; +import java.util.Collections; +import org.springdoc.core.models.GroupedOpenApi; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@OpenAPIDefinition( + servers = {@Server(url = "/")}, + info = @Info( + title = "리딩로그 API 명세서", + description = "독서 기록 서비스 리딩로그의 API 명세서", + version = "v1.0") +) +@Configuration +public class SwaggerConfig { + + @Bean + public OpenAPI openAPI(){ + String securityRequirementName = "Bearer를 제외한 accessToken값을 넣어주세요."; + SecurityRequirement securityRequirement = new SecurityRequirement().addList(securityRequirementName); + + Components components = new Components() + .addSecuritySchemes(securityRequirementName, new SecurityScheme() + .name(securityRequirementName) + .type(SecurityScheme.Type.HTTP) + .scheme("bearer") + .bearerFormat("JWT")); + + return new OpenAPI() + .components(components) + .addSecurityItem(securityRequirement); + } +} diff --git a/src/main/java/com/api/readinglog/domain/book/controller/BookController.java b/src/main/java/com/api/readinglog/domain/book/controller/BookController.java index a7dfd7a..194ae4e 100644 --- a/src/main/java/com/api/readinglog/domain/book/controller/BookController.java +++ b/src/main/java/com/api/readinglog/domain/book/controller/BookController.java @@ -8,6 +8,8 @@ import com.api.readinglog.domain.book.dto.BookSearchApiResponse; import com.api.readinglog.domain.book.dto.BookModifyRequest; import com.api.readinglog.domain.book.service.BookService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -25,20 +27,23 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; -@RestController +@Tag(name = "Book", description = "Book API") @Slf4j +@RestController @RequestMapping("/api/books") @RequiredArgsConstructor public class BookController { private final BookService bookService; + @Operation(summary = "Find book by ID", description = "사용자가 등록한 책 정보 조회") @GetMapping("/{bookId}") public Response getBookInfo(@AuthenticationPrincipal CustomUserDetail user, @PathVariable Long bookId) { return Response.success(HttpStatus.OK, String.format("%d번 책 정보 응답 성공", bookId), bookService.getBookInfo(user.getId(), bookId)); } + @Operation(summary = "Search book", description = "책 검색") @GetMapping("/search") public Response searchBooks(@RequestParam(required = false) String q, @RequestParam(defaultValue = "1") int start) { @@ -46,6 +51,7 @@ public Response searchBooks(@RequestParam(required = fals return Response.success(HttpStatus.OK, "책 검색 성공", bookService.searchBooks(q, start)); } + @Operation(summary = "Add a new book after search", description = "책 검색 후 등록") @PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE) public Response registerBookAfterSearch(@AuthenticationPrincipal CustomUserDetail user, @RequestBody @Valid BookRegisterRequest request) { @@ -54,6 +60,7 @@ public Response registerBookAfterSearch(@AuthenticationPrincipal CustomUse return Response.success(HttpStatus.CREATED, "책 등록 성공"); } + @Operation(summary = "Add a new book direct registration", description = "책 직접 등록") @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE) public Response registerBookDirect(@AuthenticationPrincipal CustomUserDetail user, @ModelAttribute @Valid BookDirectRequest request) { @@ -62,6 +69,7 @@ public Response registerBookDirect(@AuthenticationPrincipal CustomUserDeta return Response.success(HttpStatus.CREATED, "책 등록 성공"); } + @Operation(summary = "Modify book info", description = "책 정보 수정") @PatchMapping("/{bookId}") public Response modifyBook(@AuthenticationPrincipal CustomUserDetail user, @ModelAttribute BookModifyRequest bookModifyRequest, @@ -71,6 +79,7 @@ public Response modifyBook(@AuthenticationPrincipal CustomUserDetail user, return Response.success(HttpStatus.OK, "책 수정 성공"); } + @Operation(summary = "Delete book", description = "책 삭제") @DeleteMapping("/{bookId}") public Response deleteBook(@AuthenticationPrincipal CustomUserDetail user, @PathVariable Long bookId) { // TODO: 책이 삭제될 때 해당 책에 관한 기록, 포스트 함께 삭제? diff --git a/src/main/java/com/api/readinglog/domain/highlight/controller/HighlightController.java b/src/main/java/com/api/readinglog/domain/highlight/controller/HighlightController.java index a167c48..e55a109 100644 --- a/src/main/java/com/api/readinglog/domain/highlight/controller/HighlightController.java +++ b/src/main/java/com/api/readinglog/domain/highlight/controller/HighlightController.java @@ -6,6 +6,8 @@ import com.api.readinglog.domain.highlight.controller.dto.request.WriteRequest; import com.api.readinglog.domain.highlight.controller.dto.response.HighlightResponse; import com.api.readinglog.domain.highlight.service.HighlightService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; import java.util.List; import lombok.RequiredArgsConstructor; @@ -21,6 +23,7 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +@Tag(name = "Highlight", description = "Highlight API") @Slf4j @RestController @RequestMapping("/api/highlights") @@ -29,6 +32,7 @@ public class HighlightController { private final HighlightService highlightService; + @Operation(summary = "Find highlights", description = "내가 쓴 하이라이트 목록 조회") @GetMapping("/{bookId}/me") public Response> highlights(@AuthenticationPrincipal CustomUserDetail user, @PathVariable Long bookId) { @@ -37,6 +41,7 @@ public Response> highlights(@AuthenticationPrincipal Cus return Response.success(HttpStatus.OK, "내가 쓴 하이라이트 목록 조회 성공", response); } + @Operation(summary = "Add a new highlight", description = "하이라이트 작성") @PostMapping("/{bookId}") public Response write(@AuthenticationPrincipal CustomUserDetail user, @PathVariable Long bookId, @@ -47,6 +52,7 @@ public Response write(@AuthenticationPrincipal CustomUserDetail user, return Response.success(HttpStatus.CREATED, "하이라이트 작성 성공"); } + @Operation(summary = "Modify highlight", description = "하이라이트 수정") @PatchMapping("/{highlightId}") public Response modify(@AuthenticationPrincipal CustomUserDetail user, @PathVariable Long highlightId, @@ -56,6 +62,7 @@ public Response modify(@AuthenticationPrincipal CustomUserDetail user, return Response.success(HttpStatus.OK, "하이라이트 수정 성공"); } + @Operation(summary = "Delete highlight", description = "하이라이트 삭제") @DeleteMapping("/{highlightId}") public Response delete(@AuthenticationPrincipal CustomUserDetail user, @PathVariable Long highlightId) { diff --git a/src/main/java/com/api/readinglog/domain/member/controller/MemberController.java b/src/main/java/com/api/readinglog/domain/member/controller/MemberController.java index 4360dd1..5d6891c 100644 --- a/src/main/java/com/api/readinglog/domain/member/controller/MemberController.java +++ b/src/main/java/com/api/readinglog/domain/member/controller/MemberController.java @@ -15,6 +15,8 @@ import com.api.readinglog.domain.member.controller.dto.request.UpdateProfileRequest; import com.api.readinglog.domain.member.controller.dto.response.MemberDetailsResponse; import com.api.readinglog.domain.member.service.MemberService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import jakarta.validation.Valid; @@ -31,10 +33,11 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -@RestController +@Tag(name = "Member", description = "Member API") @Slf4j -@RequiredArgsConstructor +@RestController @RequestMapping("/api/members") +@RequiredArgsConstructor public class MemberController { private final MemberService memberService; @@ -46,12 +49,14 @@ public Response join_nickname(@ModelAttribute @Valid JoinNicknameRequest r return Response.success(HttpStatus.OK, "닉네임 검사 통과!"); } + @Operation(summary = "Create member", description = "일반 회원가입") @PostMapping("/join") public Response join(@ModelAttribute @Valid JoinRequest request) { memberService.join(request); return Response.success(HttpStatus.CREATED, "회원 가입 완료"); } + @Operation(summary = "Login member into the system", description = "일반 로그인") @PostMapping("/login") public Response login(@RequestBody LoginRequest request, HttpServletResponse response) { JwtToken jwtToken = memberService.login(request); @@ -66,6 +71,7 @@ public Response findMember(@AuthenticationPrincipal Custo return Response.success(HttpStatus.OK, "회원 조회 성공!", member); } + @Operation(summary = "Updated member", description = "회원정보 수정") @PatchMapping("/me") public Response updateProfile(@AuthenticationPrincipal CustomUserDetail user, @ModelAttribute @Valid UpdateProfileRequest request) { @@ -73,6 +79,7 @@ public Response updateProfile(@AuthenticationPrincipal CustomUserDetail us return Response.success(HttpStatus.OK, "회원 수정 성공!"); } + @Operation(summary = "Logout member into the system", description = "로그아웃") @PostMapping("/logout") public Response logout(HttpServletRequest request, HttpServletResponse response) { String refreshToken = CookieUtils.extractRefreshToken(request); @@ -80,6 +87,7 @@ public Response logout(HttpServletRequest request, HttpServletResponse res return Response.success(HttpStatus.OK, "로그아웃 성공!"); } + @Operation(summary = "Deleted member", description = "일반 회원 탈퇴") @DeleteMapping("/me") public Response deleteMember(@AuthenticationPrincipal CustomUserDetail user, @RequestBody DeleteRequest request) { diff --git a/src/main/java/com/api/readinglog/domain/record/controller/RecordController.java b/src/main/java/com/api/readinglog/domain/record/controller/RecordController.java index 8d42ade..3ee2189 100644 --- a/src/main/java/com/api/readinglog/domain/record/controller/RecordController.java +++ b/src/main/java/com/api/readinglog/domain/record/controller/RecordController.java @@ -6,6 +6,8 @@ import com.api.readinglog.domain.record.controller.dto.request.RecordWriteRequest; import com.api.readinglog.domain.record.controller.dto.response.RecordResponse; import com.api.readinglog.domain.record.service.RecordService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; import java.util.List; import lombok.RequiredArgsConstructor; @@ -21,6 +23,7 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +@Tag(name = "Record", description = "Record API") @RestController @RequestMapping("/api/records") @RequiredArgsConstructor @@ -29,6 +32,7 @@ public class RecordController { private final RecordService recordService; + @Operation(summary = "Find records", description = "독서 기록 조회") @GetMapping("/{bookId}") public Response> getRecord(@AuthenticationPrincipal CustomUserDetail user, @PathVariable Long bookId) { @@ -36,6 +40,7 @@ public Response> getRecord(@AuthenticationPrincipal CustomU return Response.success(HttpStatus.OK, "독서 기록 조회 성공", response); } + @Operation(summary = "Add a new record", description = "독서 기록 추가") @PostMapping("/{bookId}") public Response addRecord(@AuthenticationPrincipal CustomUserDetail user, @PathVariable Long bookId, @@ -45,6 +50,7 @@ public Response addRecord(@AuthenticationPrincipal CustomUserDetail user, return Response.success(HttpStatus.OK, "독서 기록 추가 성공"); } + @Operation(summary = "Modify record", description = "독서 기록 수정") @PatchMapping("/{recordId}") public Response modify(@AuthenticationPrincipal CustomUserDetail user, @PathVariable Long recordId, @@ -54,6 +60,7 @@ public Response modify(@AuthenticationPrincipal CustomUserDetail user, return Response.success(HttpStatus.OK, "독서 기록 수정 성공"); } + @Operation(summary = "Delete record", description = "독서 기록 삭제") @DeleteMapping("/{recordId}") public Response delete(@AuthenticationPrincipal CustomUserDetail user, @PathVariable Long recordId) { diff --git a/src/main/java/com/api/readinglog/domain/review/controller/ReviewController.java b/src/main/java/com/api/readinglog/domain/review/controller/ReviewController.java index 7512917..f9090c9 100644 --- a/src/main/java/com/api/readinglog/domain/review/controller/ReviewController.java +++ b/src/main/java/com/api/readinglog/domain/review/controller/ReviewController.java @@ -6,6 +6,8 @@ import com.api.readinglog.domain.review.controller.dto.request.WriteRequest; import com.api.readinglog.domain.review.controller.dto.response.ReviewResponse; import com.api.readinglog.domain.review.service.ReviewService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; import java.util.List; import lombok.RequiredArgsConstructor; @@ -20,6 +22,7 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +@Tag(name = "Review", description = "Review API") @RestController @RequestMapping("/api/reviews") @RequiredArgsConstructor @@ -27,6 +30,7 @@ public class ReviewController { private final ReviewService reviewService; + @Operation(summary = "Find reviews", description = "내가 쓴 서평 목록 조회") @GetMapping("/{bookId}/me") public Response> reviews(@AuthenticationPrincipal CustomUserDetail user, @PathVariable Long bookId) { @@ -35,6 +39,7 @@ public Response> reviews(@AuthenticationPrincipal CustomUse return Response.success(HttpStatus.OK, "내가 쓴 서평 목록 조회 성공", response); } + @Operation(summary = "Add a new review", description = "서평 작성") @PostMapping("/{bookId}") public Response write(@AuthenticationPrincipal CustomUserDetail user, @PathVariable Long bookId, @@ -44,6 +49,7 @@ public Response write(@AuthenticationPrincipal CustomUserDetail user, return Response.success(HttpStatus.CREATED, "서평 작성 성공"); } + @Operation(summary = "Modify review", description = "서평 수정") @PatchMapping("/{reviewId}") public Response modify(@AuthenticationPrincipal CustomUserDetail user, @PathVariable Long reviewId, @@ -53,6 +59,7 @@ public Response modify(@AuthenticationPrincipal CustomUserDetail user, return Response.success(HttpStatus.OK, "서평 수정 성공"); } + @Operation(summary = "Delete review", description = "서평 삭제") @DeleteMapping("/{reviewId}") public Response modify(@AuthenticationPrincipal CustomUserDetail user, @PathVariable Long reviewId) { diff --git a/src/main/java/com/api/readinglog/domain/summary/controller/SummaryController.java b/src/main/java/com/api/readinglog/domain/summary/controller/SummaryController.java index 7aed2af..8da1b69 100644 --- a/src/main/java/com/api/readinglog/domain/summary/controller/SummaryController.java +++ b/src/main/java/com/api/readinglog/domain/summary/controller/SummaryController.java @@ -5,6 +5,8 @@ import com.api.readinglog.domain.summary.controller.dto.request.ModifyRequest; import com.api.readinglog.domain.summary.controller.dto.request.WriteRequest; import com.api.readinglog.domain.summary.service.SummaryService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; @@ -17,6 +19,7 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +@Tag(name = "Summary", description = "Summary API") @RestController @RequestMapping("/api/summaries") @RequiredArgsConstructor @@ -24,6 +27,7 @@ public class SummaryController { private final SummaryService summaryService; + @Operation(summary = "Add a new summary", description = "한줄평 작성") @PostMapping("/{bookId}") public Response write(@AuthenticationPrincipal CustomUserDetail user, @PathVariable Long bookId, @@ -33,6 +37,7 @@ public Response write(@AuthenticationPrincipal CustomUserDetail user, return Response.success(HttpStatus.CREATED, "한줄평 작성 성공"); } + @Operation(summary = "Modify summary", description = "한줄평 수정") @PatchMapping("/{summaryId}") public Response modify(@AuthenticationPrincipal CustomUserDetail user, @PathVariable Long summaryId, @@ -42,6 +47,7 @@ public Response modify(@AuthenticationPrincipal CustomUserDetail user, return Response.success(HttpStatus.OK, "한줄평 수정 성공"); } + @Operation(summary = "Delete summary", description = "한줄평 삭제") @DeleteMapping("/{summaryId}") public Response delete(@AuthenticationPrincipal CustomUserDetail user, @PathVariable Long summaryId) {