Skip to content

Commit

Permalink
feat: 스웨거를 통해 API 명세
Browse files Browse the repository at this point in the history
  • Loading branch information
EunChanNam committed Nov 17, 2023
1 parent 99dc0f8 commit 66b7a23
Show file tree
Hide file tree
Showing 13 changed files with 233 additions and 66 deletions.
19 changes: 7 additions & 12 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ dependencies {
//P6Spy
implementation("com.github.gavlyukovskiy:p6spy-spring-boot-starter:1.9.0")

//javax.annotation import 버그 해결
//javax.annotation
implementation 'javax.annotation:javax.annotation-api:1.3.2'

//jwt
Expand All @@ -43,24 +43,19 @@ dependencies {
runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.5'
implementation 'javax.xml.bind:jaxb-api:2.3.0' //jwt 토큰 생성시 에러 해결

//test
testImplementation 'org.springframework.boot:spring-boot-starter-test'

//JPA
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'

//spring
implementation 'org.springframework.boot:spring-boot-starter-web'

//validation
implementation 'org.springframework.boot:spring-boot-starter-security'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'

//mail
implementation 'org.springframework.boot:spring-boot-starter-mail'

//thymeleaf
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'

//lombok
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
Expand All @@ -78,14 +73,14 @@ dependencies {
//네이버 클라우드
implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE'

//Spring Security
implementation 'org.springframework.boot:spring-boot-starter-security'

// queryDSL
implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta'
annotationProcessor "com.querydsl:querydsl-apt:${dependencyManagement.importedProperties['querydsl.version']}:jakarta"
annotationProcessor "jakarta.annotation:jakarta.annotation-api"
annotationProcessor "jakarta.persistence:jakarta.persistence-api"

//swagger
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.1.0'
}

def querydslDir = "src/main/generated"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,23 +13,32 @@
import com.inq.wishhair.wesharewishhair.global.dto.response.Success;
import com.inq.wishhair.wesharewishhair.global.resolver.dto.AuthInfo;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;

@Tag(name = "Auth API")
@RestController
@RequestMapping("/api/auth")
@RequiredArgsConstructor
public class AuthController {

private final AuthService authService;

@Operation(summary = "로그인 API", description = "로그인을 한다")
@ApiResponse(responseCode = "200", useReturnTypeSchema = true)
@PostMapping("/login")
public ResponseEntity<LoginResponse> login(final @RequestBody LoginRequest loginRequest) {
LoginResponse response = authService.login(loginRequest.email(), loginRequest.pw());
return ResponseEntity.ok(response);
}

@Operation(summary = "로그아웃 API", description = "로그아웃을 한다")
@ApiResponse(responseCode = "200", useReturnTypeSchema = true)
@PostMapping("/logout")
public ResponseEntity<Success> logout(@FetchAuthInfo AuthInfo authInfo) {
public ResponseEntity<Success> logout(@Parameter(hidden = true) @FetchAuthInfo AuthInfo authInfo) {
authService.logout(authInfo.userId());
return ResponseEntity.ok(new Success());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,13 @@
import com.inq.wishhair.wesharewishhair.user.domain.entity.Email;
import com.inq.wishhair.wesharewishhair.user.application.utils.UserValidator;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;

@Tag(name = "Auth API")
@RestController
@RequestMapping("/api/email")
@RequiredArgsConstructor
Expand All @@ -23,27 +28,33 @@ public class MailAuthController {
private final UserValidator userValidator;
private final MailAuthService mailAuthService;

@Operation(summary = "이메일 중복체크 API", description = "이메일 중복체크를 한다")
@ApiResponse(responseCode = "200", useReturnTypeSchema = true)
@PostMapping("/check")
public ResponseEntity<Success> checkDuplicateEmail(
final @RequestBody MailRequest request
@Parameter(description = "이메일") @RequestBody MailRequest request
) {
userValidator.validateEmailIsNotDuplicated(new Email(request.email()));

return ResponseEntity.ok(new Success());
}

@Operation(summary = "인증메일 발송 API", description = "인증메일을 발송한다")
@ApiResponse(responseCode = "200", useReturnTypeSchema = true)
@PostMapping("/send")
public ResponseEntity<Success> sendAuthorizationMail(
final @RequestBody MailRequest mailRequest
@Parameter(description = "이메일") @RequestBody MailRequest mailRequest
) {
mailAuthService.requestMailAuthorization(mailRequest.email());

return ResponseEntity.ok(new Success());
}

@Operation(summary = "이메일 인증 API", description = "이메일을 인증한다")
@ApiResponse(responseCode = "200", useReturnTypeSchema = true)
@PostMapping("/validate")
public ResponseEntity<Success> authorizeKey(
final @RequestBody AuthKeyRequest request
@Parameter(description = "메일 인증 폼") @RequestBody AuthKeyRequest request
) {
mailAuthService.checkAuthCode(request.email(), request.authKey());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,24 @@
import com.inq.wishhair.wesharewishhair.global.annotation.FetchAuthInfo;
import com.inq.wishhair.wesharewishhair.global.resolver.dto.AuthInfo;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;

@Tag(name = "Auth API")
@RestController
@RequestMapping("/api")
@RequiredArgsConstructor
public class TokenReissueController {

private final TokenReissueService tokenReissueService;

@Operation(summary = "토큰 재발급 API", description = "토큰을 재발급한다")
@ApiResponse(responseCode = "200", description = "엑세스 토큰 + 리프레쉬 토큰", useReturnTypeSchema = true)
@PostMapping("/tokens/reissue")
public ResponseEntity<TokenResponse> reissueToken(@FetchAuthInfo AuthInfo authInfo) {
public ResponseEntity<TokenResponse> reissueToken(@Parameter(hidden = true) @FetchAuthInfo AuthInfo authInfo) {
TokenResponse response = tokenReissueService.reissueToken(authInfo.userId(), authInfo.token());
return ResponseEntity.ok(response);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package com.inq.wishhair.wesharewishhair.global.config;

import java.util.List;

import org.springdoc.core.models.GroupedOpenApi;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import io.swagger.v3.oas.annotations.OpenAPIDefinition;
import io.swagger.v3.oas.annotations.info.Info;
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;

@OpenAPIDefinition(
info = @Info(
title = "We Share Wish Hair API",
description = "We Share Wish Hair API 명세",
version = "v1"))
@Configuration
public class SwaggerConfig {

private static final String AUTHORIZATION = "Authorization";

@Bean
public OpenAPI openAPI(){
SecurityScheme securityScheme = new SecurityScheme()
.type(SecurityScheme.Type.HTTP).scheme("bearer").bearerFormat("JWT")
.in(SecurityScheme.In.HEADER).name(AUTHORIZATION);
SecurityRequirement securityRequirement = new SecurityRequirement().addList(AUTHORIZATION);

return new OpenAPI()
.components(new Components().addSecuritySchemes(AUTHORIZATION, securityScheme))
.security(List.of(securityRequirement));
}

@Bean
public GroupedOpenApi chatOpenApi() {
String[] paths = {"/api/**"};

return GroupedOpenApi.builder()
.group("API v1")
.pathsToMatch(paths)
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,38 +18,50 @@
import com.inq.wishhair.wesharewishhair.hairstyle.application.dto.response.HairStyleSimpleResponse;
import com.inq.wishhair.wesharewishhair.hairstyle.domain.hashtag.Tag;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import lombok.RequiredArgsConstructor;

@io.swagger.v3.oas.annotations.tags.Tag(name = "HairStyle API")
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/hair_styles")
public class HairStyleSearchController {

private final HairStyleSearchService hairStyleSearchService;

@Operation(summary = "헤어스타일 추천 API", description = "헤어스타일 추천을 한다")
@ApiResponse(responseCode = "200", useReturnTypeSchema = true)
@GetMapping("/recommend")
public ResponseWrapper<HairStyleResponse> respondRecommendedHairStyle(
@RequestParam(defaultValue = "ERROR") List<Tag> tags,
@FetchAuthInfo AuthInfo authInfo
@Parameter(description = "태그 정보") @RequestParam(defaultValue = "ERROR") List<Tag> tags,
@Parameter(hidden = true) @FetchAuthInfo AuthInfo authInfo
) {
return hairStyleSearchService.recommendHair(tags, authInfo.userId());
}

@Operation(summary = "얼굴형에 맞는 헤어스타일 추천 API", description = "얼굴형 정보로만 헤어스타일을 추천한다")
@ApiResponse(responseCode = "200", useReturnTypeSchema = true)
@GetMapping("/home")
public ResponseWrapper<HairStyleResponse> findHairStyleByFaceShape(
@FetchAuthInfo AuthInfo authInfo
@Parameter(hidden = true) @FetchAuthInfo AuthInfo authInfo
) {
return hairStyleSearchService.recommendHairByFaceShape(authInfo.userId());
}

@Operation(summary = "찜한 헤어스타일 조회 API", description = "찜한 헤어스타일을 조회한다")
@ApiResponse(responseCode = "200", useReturnTypeSchema = true)
@GetMapping("/wish")
public PagedResponse<HairStyleResponse> findWishHairStyles(
@FetchAuthInfo AuthInfo authInfo,
@PageableDefault Pageable pageable) {
@Parameter(hidden = true) @FetchAuthInfo AuthInfo authInfo,
@Parameter(description = "페이징 정보") @PageableDefault Pageable pageable) {

return hairStyleSearchService.findWishHairStyles(authInfo.userId(), pageable);
}

@Operation(summary = "모든 헤어스타일 API", description = "모든 헤어스타일의 간략한 정보를 조회한다")
@ApiResponse(responseCode = "200", useReturnTypeSchema = true)
@GetMapping
public ResponseWrapper<HairStyleSimpleResponse> findAllHairStyles() {
return hairStyleSearchService.findAllHairStyle();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,40 +14,50 @@
import com.inq.wishhair.wesharewishhair.hairstyle.application.WishHairService;
import com.inq.wishhair.wesharewishhair.hairstyle.application.dto.response.WishHairResponse;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;

@Tag(name = "HairStyle API")
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/hair_styles/wish/")
public class WishHairController {

private final WishHairService wishHairService;

@Operation(summary = "찜 API", description = "찜를 한다")
@ApiResponse(responseCode = "200", useReturnTypeSchema = true)
@PostMapping(path = "{hairStyleId}")
public ResponseEntity<Success> executeWish(
@PathVariable Long hairStyleId,
@FetchAuthInfo AuthInfo authInfo
@Parameter(description = "헤어스타일 아이디") @PathVariable Long hairStyleId,
@Parameter(hidden = true) @FetchAuthInfo AuthInfo authInfo
) {

wishHairService.executeWish(hairStyleId, authInfo.userId());

return ResponseEntity.ok(new Success());
}

@Operation(summary = "찜 취소 API", description = "찜를 취소한다")
@ApiResponse(responseCode = "200", useReturnTypeSchema = true)
@DeleteMapping(path = "{hairStyleId}")
public ResponseEntity<Success> cancelWish(
@PathVariable Long hairStyleId,
@FetchAuthInfo AuthInfo authInfo
@Parameter(description = "헤어스타일 아이디") @PathVariable Long hairStyleId,
@Parameter(hidden = true) @FetchAuthInfo AuthInfo authInfo
) {
wishHairService.cancelWish(hairStyleId, authInfo.userId());

return ResponseEntity.ok(new Success());
}

@Operation(summary = "헤어스타일 찜 확인 API", description = "찜를 취소한다")
@ApiResponse(responseCode = "200", useReturnTypeSchema = true)
@GetMapping(path = {"{hairStyleId}"})
public ResponseEntity<WishHairResponse> checkIsWishing(
@PathVariable Long hairStyleId,
@FetchAuthInfo AuthInfo authInfo
@Parameter(description = "헤어스타일 아이디") @PathVariable Long hairStyleId,
@Parameter(hidden = true) @FetchAuthInfo AuthInfo authInfo
) {
WishHairResponse result = wishHairService.checkIsWishing(hairStyleId, authInfo.userId());
return ResponseEntity.ok(result);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,33 +18,40 @@
import com.inq.wishhair.wesharewishhair.point.application.dto.PointUseRequest;
import com.inq.wishhair.wesharewishhair.point.application.PointService;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;

@Tag(name = "Point API")
@RestController
@RequestMapping("/api/points")
@RequiredArgsConstructor
public class PointController {

private final PointService pointService;
private final PointSearchService pointSearchService;

@Operation(summary = "포인트 사용 API", description = "포인트를 사용한다")
@ApiResponse(responseCode = "200", useReturnTypeSchema = true)
@PostMapping("/use")
public ResponseEntity<Success> usePoint(
final @RequestBody PointUseRequest request,
final @FetchAuthInfo AuthInfo authInfo
@Parameter(description = "포인트 사용 폼") @RequestBody PointUseRequest request,
@Parameter(hidden = true) @FetchAuthInfo AuthInfo authInfo
) {
pointService.usePoint(request, authInfo.userId());

return ResponseEntity.ok(new Success());
}

private final PointSearchService pointSearchService;

@Operation(summary = "포인트 내역 조회 API", description = "포인트 내역을 조회한다")
@ApiResponse(responseCode = "200", useReturnTypeSchema = true)
@GetMapping
public ResponseEntity<PagedResponse<PointResponse>> findPointHistories(
final @FetchAuthInfo AuthInfo authInfo,
final @PageableDefault Pageable pageable
@Parameter(hidden = true) @FetchAuthInfo AuthInfo authInfo,
@Parameter(description = "페이징 정보") @PageableDefault Pageable pageable
) {

return ResponseEntity.ok(pointSearchService.getPointHistories(authInfo.userId(), pageable));
}
}
Loading

0 comments on commit 66b7a23

Please sign in to comment.