-
Notifications
You must be signed in to change notification settings - Fork 3
[feat] SSE 이벤트 추가 #598
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[feat] SSE 이벤트 추가 #598
Changes from 41 commits
ab6fd81
26f676b
8ad34de
009c128
07ce543
2c99398
07948a9
7da7f5a
6f5f56c
3d70c3c
3674410
e2c4a60
7c924f9
bbb4157
b4b9857
2188d80
a2be5c8
c008e4b
950828e
9dd5924
89692ae
875c3d7
411b83a
24c5a17
48e2fee
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| <?xml version="1.0" encoding="utf-8"?> | ||
| <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" | ||
| xmlns:app="http://schemas.android.com/apk/res-auto" | ||
| xmlns:tools="http://schemas.android.com/tools" | ||
| android:id="@+id/main" | ||
| android:layout_width="match_parent" | ||
| android:layout_height="match_parent" | ||
| tools:context=".ui.login.LoginActivity"> | ||
|
|
||
| </androidx.constraintlayout.widget.ConstraintLayout> | ||
eunseongu marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,62 @@ | ||
| package turip.account.controller; | ||
|
|
||
| import io.swagger.v3.oas.annotations.Operation; | ||
| import io.swagger.v3.oas.annotations.Parameter; | ||
| import io.swagger.v3.oas.annotations.media.Content; | ||
| import io.swagger.v3.oas.annotations.media.ExampleObject; | ||
| import io.swagger.v3.oas.annotations.media.Schema; | ||
| import io.swagger.v3.oas.annotations.responses.ApiResponse; | ||
| import io.swagger.v3.oas.annotations.responses.ApiResponses; | ||
| import io.swagger.v3.oas.annotations.tags.Tag; | ||
| import lombok.RequiredArgsConstructor; | ||
| import org.springframework.http.ResponseEntity; | ||
| import org.springframework.web.bind.annotation.DeleteMapping; | ||
| import org.springframework.web.bind.annotation.RequestMapping; | ||
| import org.springframework.web.bind.annotation.RestController; | ||
| import turip.account.domain.Guest; | ||
| import turip.account.service.GuestService; | ||
| import turip.auth.resolver.AuthGuest; | ||
| import turip.common.exception.ErrorResponse; | ||
|
|
||
| @RestController | ||
| @RequiredArgsConstructor | ||
| @RequestMapping("/guests") | ||
| @Tag(name = "Guest", description = "게스트 API") | ||
| public class GuestController { | ||
|
|
||
| private final GuestService guestService; | ||
|
|
||
| @Operation( | ||
| summary = "게스트 탈퇴 api", | ||
| description = "게스트를 삭제한다." | ||
| ) | ||
| @ApiResponses(value = { | ||
| @ApiResponse( | ||
| responseCode = "204", | ||
| description = "성공 예시" | ||
| ), | ||
| @ApiResponse( | ||
| responseCode = "400", | ||
| description = "실패 예시", | ||
| content = @Content( | ||
| mediaType = "application/json", | ||
| schema = @Schema(implementation = ErrorResponse.class), | ||
| examples = @ExampleObject( | ||
| name = "guest not found", | ||
| summary = "요청 헤더에 device-fid가 존재하지 않는 경우", | ||
| value = """ | ||
| { | ||
| "tag": "DEVICE_FID_REQUIRED", | ||
| "message": "요청 헤더에 device_fid가 존재하지 않습니다." | ||
| } | ||
| """ | ||
| ) | ||
| ) | ||
| ) | ||
| }) | ||
| @DeleteMapping("/me") | ||
| public ResponseEntity<Void> delete(@Parameter(hidden = true) @AuthGuest Guest guest) { | ||
| guestService.delete(guest); | ||
| return ResponseEntity.noContent().build(); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,144 @@ | ||
| package turip.account.controller; | ||
|
|
||
| import io.swagger.v3.oas.annotations.Operation; | ||
| import io.swagger.v3.oas.annotations.Parameter; | ||
| import io.swagger.v3.oas.annotations.media.Content; | ||
| import io.swagger.v3.oas.annotations.media.ExampleObject; | ||
| import io.swagger.v3.oas.annotations.media.Schema; | ||
| import io.swagger.v3.oas.annotations.responses.ApiResponse; | ||
| import io.swagger.v3.oas.annotations.responses.ApiResponses; | ||
| import io.swagger.v3.oas.annotations.tags.Tag; | ||
| import lombok.RequiredArgsConstructor; | ||
| import org.springframework.http.ResponseEntity; | ||
| import org.springframework.web.bind.annotation.DeleteMapping; | ||
| import org.springframework.web.bind.annotation.PostMapping; | ||
| import org.springframework.web.bind.annotation.RequestMapping; | ||
| import org.springframework.web.bind.annotation.RestController; | ||
| import turip.auth.resolver.AuthGuest; | ||
| import turip.auth.resolver.AuthMember; | ||
| import turip.common.exception.ErrorResponse; | ||
| import turip.account.domain.Guest; | ||
| import turip.account.domain.Member; | ||
| import turip.account.service.MemberService; | ||
|
|
||
| @RestController | ||
| @RequiredArgsConstructor | ||
| @RequestMapping("/members") | ||
| @Tag(name = "Member", description = "회원 API") | ||
| public class MemberController { | ||
|
|
||
| private final MemberService memberService; | ||
|
|
||
| @Operation( | ||
| summary = "마이그레이션 api", | ||
| description = "게스트의 데이터를 멤버의 데이터로 마이그레이션한다." | ||
| ) | ||
| @ApiResponses(value = { | ||
| @ApiResponse( | ||
| responseCode = "204", | ||
| description = "성공 예시" | ||
| ), | ||
| @ApiResponse( | ||
| responseCode = "401", | ||
| description = "실패 예시", | ||
| content = @Content( | ||
| mediaType = "application/json", | ||
| schema = @Schema(implementation = ErrorResponse.class), | ||
| examples = { | ||
| @ExampleObject( | ||
| name = "access token expired", | ||
| summary = "만료된 access token", | ||
| value = """ | ||
| { | ||
| "tag": "ACCESS_TOKEN_EXPIRED", | ||
| "message": "access token이 만료됐습니다." | ||
| } | ||
| """ | ||
| ), | ||
| @ExampleObject( | ||
| name = "invalid signature access token", | ||
| summary = "서명값이 올바르지 않은 access token", | ||
| value = """ | ||
| { | ||
| "tag": "ACCESS_TOKEN_SIGNATURE_INVALID", | ||
| "message": "access token이 위조됐습니다." | ||
| } | ||
| """ | ||
| ), | ||
| @ExampleObject( | ||
| name = "unauthorized", | ||
| summary = "알 수 없는 이유로 인증 실패", | ||
| value = """ | ||
| { | ||
| "tag": "UNAUTHORIZED", | ||
| "message": "토큰 기반 인증에 실패했습니다." | ||
| } | ||
| """ | ||
| ) | ||
| } | ||
| ) | ||
| ) | ||
| }) | ||
| @PostMapping("/migration") | ||
| public ResponseEntity<Void> migrate(@Parameter(hidden = true) @AuthMember Member member, | ||
| @Parameter(hidden = true) @AuthGuest Guest guest) { | ||
| memberService.migrate(member, guest); | ||
| return ResponseEntity.noContent().build(); | ||
| } | ||
|
|
||
| @Operation( | ||
| summary = "회원 탈퇴 api", | ||
| description = "회원을 삭제한다." | ||
| ) | ||
| @ApiResponses(value = { | ||
| @ApiResponse( | ||
| responseCode = "204", | ||
| description = "성공 예시" | ||
| ), | ||
| @ApiResponse( | ||
| responseCode = "401", | ||
| description = "실패 예시", | ||
| content = @Content( | ||
| mediaType = "application/json", | ||
| schema = @Schema(implementation = ErrorResponse.class), | ||
| examples = { | ||
| @ExampleObject( | ||
| name = "access token expired", | ||
| summary = "만료된 access token", | ||
| value = """ | ||
| { | ||
| "tag": "ACCESS_TOKEN_EXPIRED", | ||
| "message": "access token이 만료됐습니다." | ||
| } | ||
| """ | ||
| ), | ||
| @ExampleObject( | ||
| name = "invalid signature access token", | ||
| summary = "서명값이 올바르지 않은 access token", | ||
| value = """ | ||
| { | ||
| "tag": "ACCESS_TOKEN_SIGNATURE_INVALID", | ||
| "message": "access token이 위조됐습니다." | ||
| } | ||
| """ | ||
| ), | ||
| @ExampleObject( | ||
| name = "unauthorized", | ||
| summary = "알 수 없는 이유로 인증 실패", | ||
| value = """ | ||
| { | ||
| "tag": "UNAUTHORIZED", | ||
| "message": "토큰 기반 인증에 실패했습니다." | ||
| } | ||
| """ | ||
| ) | ||
| } | ||
| ) | ||
| ) | ||
| }) | ||
| @DeleteMapping("/me") | ||
| public ResponseEntity<Void> delete(@Parameter(hidden = true) @AuthMember Member member) { | ||
| memberService.delete(member); | ||
| return ResponseEntity.noContent().build(); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,26 @@ | ||||||
| package turip.account.domain; | ||||||
|
|
||||||
| import jakarta.persistence.Entity; | ||||||
| import jakarta.persistence.GeneratedValue; | ||||||
| import jakarta.persistence.GenerationType; | ||||||
| import jakarta.persistence.Id; | ||||||
| import jakarta.persistence.Table; | ||||||
| import lombok.AccessLevel; | ||||||
| import lombok.AllArgsConstructor; | ||||||
| import lombok.EqualsAndHashCode; | ||||||
| import lombok.Getter; | ||||||
| import lombok.NoArgsConstructor; | ||||||
|
|
||||||
| @Entity | ||||||
| @Getter | ||||||
| @AllArgsConstructor | ||||||
| @Table(name = "account") | ||||||
| @NoArgsConstructor(access = AccessLevel.PUBLIC) | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. JPA no-args 생성자 접근 레벨을 JPA는 🛡️ 제안 수정-@NoArgsConstructor(access = AccessLevel.PUBLIC)
+@NoArgsConstructor(access = AccessLevel.PROTECTED)📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||
| @EqualsAndHashCode(onlyExplicitlyIncluded = true) | ||||||
| public class Account { | ||||||
|
|
||||||
| @Id | ||||||
| @EqualsAndHashCode.Include | ||||||
| @GeneratedValue(strategy = GenerationType.IDENTITY) | ||||||
| private Long id; | ||||||
| } | ||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| package turip.account.domain; | ||
|
|
||
| import jakarta.persistence.Column; | ||
| import jakarta.persistence.Entity; | ||
| import jakarta.persistence.EnumType; | ||
| import jakarta.persistence.Enumerated; | ||
| import jakarta.persistence.FetchType; | ||
| import jakarta.persistence.ForeignKey; | ||
| import jakarta.persistence.GeneratedValue; | ||
| import jakarta.persistence.GenerationType; | ||
| import jakarta.persistence.Id; | ||
| import jakarta.persistence.JoinColumn; | ||
| import jakarta.persistence.OneToOne; | ||
| import jakarta.persistence.Table; | ||
| import jakarta.persistence.UniqueConstraint; | ||
| import lombok.AccessLevel; | ||
| import lombok.AllArgsConstructor; | ||
| import lombok.EqualsAndHashCode; | ||
| import lombok.Getter; | ||
| import lombok.NoArgsConstructor; | ||
|
|
||
| @Entity | ||
| @Getter | ||
| @AllArgsConstructor | ||
| @Table(name = "member", uniqueConstraints = { | ||
| @UniqueConstraint(name = "uq_member__provider_provider_id", columnNames = {"provider", "provider_id"}) | ||
| }) | ||
| @EqualsAndHashCode(onlyExplicitlyIncluded = true) | ||
| @NoArgsConstructor(access = AccessLevel.PROTECTED) | ||
| public class Member { | ||
|
|
||
| @Id | ||
| @EqualsAndHashCode.Include | ||
| @GeneratedValue(strategy = GenerationType.IDENTITY) | ||
| private Long id; | ||
|
|
||
| @OneToOne(fetch = FetchType.LAZY) | ||
| @JoinColumn(name = "account_id", nullable = false, unique = true, foreignKey = @ForeignKey(name = "fk_member__account")) | ||
| private Account account; | ||
|
|
||
| @Enumerated(EnumType.STRING) | ||
| @Column(name = "provider", nullable = false) | ||
| private Provider provider; | ||
|
|
||
| @Column(name = "provider_id", nullable = false) | ||
| private String providerId; | ||
|
|
||
| @Column(name = "email") | ||
| private String email; | ||
|
|
||
| public Member(Account account, Provider provider, String providerId, String email) { | ||
| this.account = account; | ||
| this.provider = provider; | ||
| this.providerId = providerId; | ||
| this.email = email; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| package turip.account.domain; | ||
|
|
||
| public enum Provider { | ||
|
|
||
| GOOGLE, KAKAO; | ||
| } | ||
eunseongu marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| package turip.account.repository; | ||
|
|
||
| import org.springframework.data.jpa.repository.JpaRepository; | ||
| import turip.account.domain.Account; | ||
|
|
||
| public interface AccountRepository extends JpaRepository<Account, Long> { | ||
|
|
||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| package turip.account.repository; | ||
|
|
||
| import java.util.Optional; | ||
| import org.springframework.data.jpa.repository.JpaRepository; | ||
| import turip.account.domain.Guest; | ||
|
|
||
| public interface GuestRepository extends JpaRepository<Guest, Long> { | ||
|
|
||
| Optional<Guest> findByDeviceFid(String deviceFid); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| package turip.account.repository; | ||
|
|
||
| import java.util.Optional; | ||
| import org.springframework.data.jpa.repository.JpaRepository; | ||
| import org.springframework.stereotype.Repository; | ||
| import turip.account.domain.Member; | ||
| import turip.account.domain.Provider; | ||
|
|
||
| @Repository | ||
| public interface MemberRepository extends JpaRepository<Member, Long> { | ||
|
|
||
| boolean existsByProviderAndProviderId(Provider provider, String providerId); | ||
|
|
||
| Optional<Member> findByProviderAndProviderId(Provider provider, String providerId); | ||
|
|
||
| Optional<Member> findByAccountId(Long accountId); | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.