Skip to content

Commit

Permalink
๐Ÿ“ Docs: Swagger ์„ค์ • (#31)
Browse files Browse the repository at this point in the history
* ๐Ÿ“ Docs: Swagger ์„ค์ •

* ๐Ÿ“ Docs: Auth API ๋ฌธ์„œ ์ž‘์„ฑ
  • Loading branch information
ahnsugyeong authored Apr 13, 2024
1 parent e2d8f87 commit 54664c4
Show file tree
Hide file tree
Showing 25 changed files with 407 additions and 100 deletions.
3 changes: 3 additions & 0 deletions moodoodle-api/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ dependencies {
implementation 'io.jsonwebtoken:jjwt-api:0.11.5'
implementation 'io.jsonwebtoken:jjwt-impl:0.11.5'
implementation 'io.jsonwebtoken:jjwt-jackson:0.11.5'

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

tasks.named('bootJar') {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,11 @@
import zzangdol.moodoodleapi.auth.presentation.dto.request.EmailVerificationRequest;
import zzangdol.moodoodleapi.auth.presentation.dto.request.SignInRequest;
import zzangdol.moodoodleapi.auth.presentation.dto.request.SignUpRequest;
import zzangdol.moodoodleapi.auth.presentation.dto.response.EmailVerificationTokenResponse;
import zzangdol.moodoodleapi.jwt.JwtResponse;
import zzangdol.moodoodleapi.jwt.JwtService;
import zzangdol.moodoodlecommon.exception.custom.MemberCredentialsException;
import zzangdol.moodoodlecommon.response.status.ErrorStatus;
import zzangdol.ses.service.AwsSesService;

@Component
Expand All @@ -23,16 +26,25 @@ public class AuthFacade {
private final EmailVerificationTokenService emailVerificationTokenService;

public boolean sendVerificationEmail(String email) {
if (!authService.isEmailAvailable(email)) {
throw new MemberCredentialsException(ErrorStatus.EMAIL_ALREADY_EXISTS);
}
String verificationCode = verificationCodeService.generateAndSaveCode(email);
return awsSesService.sendVerificationEmail(email, verificationCode);
}

public String verifyEmail(EmailVerificationRequest request) {
public EmailVerificationTokenResponse verifyEmail(EmailVerificationRequest request) {
if (!authService.isEmailAvailable(request.getEmail())) {
throw new MemberCredentialsException(ErrorStatus.EMAIL_ALREADY_EXISTS);
}
verificationCodeService.verifyCode(request.getEmail(), request.getCode());
return emailVerificationTokenService.generateAndSaveCode(request.getEmail());
}

public JwtResponse signUp(String emailVerificationToken, SignUpRequest request) {
if (!authService.isEmailAvailable(request.getEmail())) {
throw new MemberCredentialsException(ErrorStatus.EMAIL_ALREADY_EXISTS);
}
emailVerificationTokenService.verifyToken(request.getEmail(), emailVerificationToken);
return authService.signUp(request);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import java.util.Base64;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import zzangdol.moodoodleapi.auth.presentation.dto.response.EmailVerificationTokenResponse;
import zzangdol.moodoodlecommon.exception.custom.EmailVerificationTokenException;
import zzangdol.moodoodlecommon.response.status.ErrorStatus;
import zzangdol.redis.dao.EmailVerificationTokenRepository;
Expand All @@ -15,10 +16,12 @@ public class EmailVerificationTokenService {

private final EmailVerificationTokenRepository emailVerificationTokenRepository;

public String generateAndSaveCode(String id) {
public EmailVerificationTokenResponse generateAndSaveCode(String id) {
String token = generateSecureToken();
saveEmailVerificationToken(id, token, 86400L);
return token;
return EmailVerificationTokenResponse.builder()
.emailVerificationToken(token)
.build();
}

private String generateSecureToken() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package zzangdol.moodoodleapi.auth.presentation;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
Expand All @@ -12,45 +15,90 @@
import zzangdol.moodoodleapi.auth.presentation.dto.request.EmailVerificationRequest;
import zzangdol.moodoodleapi.auth.presentation.dto.request.SignInRequest;
import zzangdol.moodoodleapi.auth.presentation.dto.request.SignUpRequest;
import zzangdol.moodoodleapi.auth.presentation.dto.response.EmailVerificationTokenResponse;
import zzangdol.moodoodleapi.global.annotation.ApiErrorCodeExample;
import zzangdol.moodoodleapi.jwt.JwtResponse;
import zzangdol.moodoodlecommon.response.ApiResponse;
import zzangdol.moodoodlecommon.response.ResponseDto;
import zzangdol.moodoodlecommon.response.status.ErrorStatus;

@RequiredArgsConstructor

@ApiResponse(responseCode = "2000", description = "์„ฑ๊ณต")
@Tag(name = "1๏ธโƒฃ Auth API", description = "์ธ์ฆ API")
@RequestMapping("/api/auth")
@RestController
public class AuthController {

private final AuthFacade authFacade;

@ApiErrorCodeExample({
ErrorStatus.EMAIL_ALREADY_EXISTS,
ErrorStatus.INTERNAL_SERVER_ERROR
})
@Operation(summary = "์ธ์ฆ ์ด๋ฉ”์ผ ๋ฐœ์†ก", description = "์ง€์ •๋œ ์ด๋ฉ”์ผ ์ฃผ์†Œ๋กœ ์ธ์ฆ ์ด๋ฉ”์ผ์„ ๋ฐœ์†กํ•ฉ๋‹ˆ๋‹ค.")
@PostMapping("/send-verification-email")
public ApiResponse<Boolean> sendVerificationEmail(@RequestParam String email) {
return ApiResponse.onSuccess(authFacade.sendVerificationEmail(email));
public ResponseDto<Boolean> sendVerificationEmail(@RequestParam String email) {
return ResponseDto.onSuccess(authFacade.sendVerificationEmail(email));
}

@ApiErrorCodeExample({
ErrorStatus.TOKEN_INVALID,
ErrorStatus.TOKEN_EXPIRED,
ErrorStatus.EMAIL_ALREADY_EXISTS,
ErrorStatus.INTERNAL_SERVER_ERROR
})
@Operation(summary = "์ด๋ฉ”์ผ ๊ฒ€์ฆ", description = "์ œ๊ณต๋œ ๊ฒ€์ฆ ์ฝ”๋“œ๋กœ ์‚ฌ์šฉ์ž์˜ ์ด๋ฉ”์ผ์„ ๊ฒ€์ฆํ•ฉ๋‹ˆ๋‹ค.")
@PostMapping("/verify-email")
public ApiResponse<String> verifyEmail(@RequestBody EmailVerificationRequest request) {
return ApiResponse.onSuccess(authFacade.verifyEmail(request));
public ResponseDto<EmailVerificationTokenResponse> verifyEmail(@RequestBody EmailVerificationRequest request) {
return ResponseDto.onSuccess(authFacade.verifyEmail(request));
}

@ApiErrorCodeExample({
ErrorStatus.TOKEN_INVALID,
ErrorStatus.MEMBER_NOT_FOUND,
ErrorStatus.EMAIL_ALREADY_EXISTS,
ErrorStatus.INTERNAL_SERVER_ERROR
})
@Operation(summary = "์‚ฌ์šฉ์ž ํšŒ์›๊ฐ€์ž…", description = "์ด๋ฉ”์ผ ๊ฒ€์ฆ ํ† ํฐ์„ ์‚ฌ์šฉํ•˜์—ฌ ์ƒˆ๋กœ์šด ์‚ฌ์šฉ์ž๋ฅผ ๋“ฑ๋กํ•ฉ๋‹ˆ๋‹ค.")
@PostMapping("/sign-up")
public ApiResponse<JwtResponse> signUp(@RequestHeader("X-Email-Verification-Token") String emailVerificationToken,
public ResponseDto<JwtResponse> signUp(@RequestHeader("X-Email-Verification-Token") String emailVerificationToken,
@RequestBody SignUpRequest request) {
return ApiResponse.onSuccess(authFacade.signUp(emailVerificationToken, request));
return ResponseDto.onSuccess(authFacade.signUp(emailVerificationToken, request));
}

@ApiErrorCodeExample({
ErrorStatus.PASSWORD_MISMATCH,
ErrorStatus.MEMBER_NOT_FOUND,
ErrorStatus.INTERNAL_SERVER_ERROR
})
@Operation(summary = "์‚ฌ์šฉ์ž ๋กœ๊ทธ์ธ", description = "์‚ฌ์šฉ์ž๋ฅผ ์ธ์ฆํ•˜๊ณ  Access Token, Refresh Token์„ ๋ฐœ๊ธ‰ํ•ฉ๋‹ˆ๋‹ค.")
@PostMapping("/sign-in")
public ApiResponse<JwtResponse> signUp(@RequestBody SignInRequest request) {
return ApiResponse.onSuccess(authFacade.signIn(request));
public ResponseDto<JwtResponse> signUp(@RequestBody SignInRequest request) {
return ResponseDto.onSuccess(authFacade.signIn(request));
}

@ApiErrorCodeExample({
ErrorStatus.MEMBER_NOT_FOUND
})
@Operation(summary = "์ด๋ฉ”์ผ ์‚ฌ์šฉ ๊ฐ€๋Šฅ ์—ฌ๋ถ€ ํ™•์ธ", description = "์ œ๊ณต๋œ ์ด๋ฉ”์ผ์ด ๋“ฑ๋ก์— ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.")
@GetMapping("/check-email")
public ApiResponse<Boolean> checkEmailAvailability(@RequestParam String email) {
return ApiResponse.onSuccess(authFacade.isEmailAvailable(email));
public ResponseDto<Boolean> checkEmailAvailability(@RequestParam String email) {
return ResponseDto.onSuccess(authFacade.isEmailAvailable(email));
}

@ApiErrorCodeExample({
ErrorStatus.REFRESH_TOKEN_NOT_FOUND,
ErrorStatus.TOKEN_INVALID,
ErrorStatus.TOKEN_EXPIRED,
ErrorStatus.TOKEN_UNSUPPORTED,
ErrorStatus.TOKEN_CLAIMS_EMPTY,
ErrorStatus.MEMBER_NOT_FOUND,
ErrorStatus.INTERNAL_SERVER_ERROR
})
@Operation(summary = "ํ† ํฐ ์žฌ๋ฐœ๊ธ‰", description = "Refresh Token, Access Token์„ ์žฌ๋ฐœ๊ธ‰ํ•ฉ๋‹ˆ๋‹ค.")
@PostMapping("/reissue")
public ApiResponse<JwtResponse> reissue(@RequestParam String refreshToken) {
return ApiResponse.onSuccess(authFacade.reissueToken(refreshToken));
public ResponseDto<JwtResponse> reissue(@RequestParam String refreshToken) {
return ResponseDto.onSuccess(authFacade.reissueToken(refreshToken));
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package zzangdol.moodoodleapi.auth.presentation.dto.request;

import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import lombok.AllArgsConstructor;
Expand All @@ -15,9 +16,11 @@ public class EmailVerificationRequest {

@NotBlank(message = "์ด๋ฉ”์ผ์€ ํ•„์ˆ˜ ์ž…๋ ฅ ๊ฐ’์ž…๋‹ˆ๋‹ค.")
@Email(message = "์œ ํšจํ•œ ์ด๋ฉ”์ผ ์ฃผ์†Œ๋ฅผ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.")
@Schema(example = "teamzzangdol@gmail.com")
private String email;

@NotBlank(message = "์ธ์ฆ ์ฝ”๋“œ๋Š” ํ•„์ˆ˜ ์ž…๋ ฅ ๊ฐ’์ž…๋‹ˆ๋‹ค.")
@Schema(example = "123456")
private String code;

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package zzangdol.moodoodleapi.auth.presentation.dto.request;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
Expand All @@ -11,7 +12,10 @@
@AllArgsConstructor
public class SignInRequest {

@Schema(example = "teamzzangdol@gmail.com")
private String email;

@Schema(example = "password1234")
private String password;

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package zzangdol.moodoodleapi.auth.presentation.dto.request;

import io.swagger.v3.oas.annotations.media.Schema;
import java.time.LocalTime;
import lombok.AllArgsConstructor;
import lombok.Builder;
Expand All @@ -15,9 +16,16 @@
@AllArgsConstructor
public class SignUpRequest {

@Schema(example = "teamzzangdol@gmail.com")
private String email;

@Schema(example = "password1234")
private String password;

@Schema(example = "์งฑ๋Œ")
private String nickname;

@Schema(example = "21:00")
private LocalTime notificationTime;

public Member toEntity(AuthProvider authProvider, Role role, String encodedPassword) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package zzangdol.moodoodleapi.auth.presentation.dto.response;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
Expand All @@ -11,6 +12,7 @@
@AllArgsConstructor
public class EmailVerificationTokenResponse {

@Schema(example = "H1i9sNC2DhTcehPkKklwgYrQ5h_b1KwC")
private String emailVerificationToken;

}

This file was deleted.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package zzangdol.moodoodleapi.global;

import io.swagger.v3.oas.annotations.Hidden;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import zzangdol.moodoodlecommon.response.ResponseDto;

@Hidden
@RestController
public class HealthCheckController {

@GetMapping("/health-check")
public ResponseDto<Boolean> healthCheck() {
return ResponseDto.onSuccess(Boolean.TRUE);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package zzangdol.moodoodleapi.global.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import zzangdol.moodoodlecommon.response.status.ErrorStatus;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiErrorCodeExample {

ErrorStatus[] value();

}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package zzangdol.moodoodleapi.security;
package zzangdol.moodoodleapi.global.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package zzangdol.moodoodleapi.security;
package zzangdol.moodoodleapi.global.annotation;

import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
Expand Down
Loading

0 comments on commit 54664c4

Please sign in to comment.