Skip to content
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

M3-293 푸시 알림 권한 동의 api 구현 #65

Merged
merged 14 commits into from
Aug 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package com.m3pro.groundflip.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.m3pro.groundflip.domain.dto.Response;
import com.m3pro.groundflip.domain.dto.permission.PermissionRequest;
import com.m3pro.groundflip.domain.dto.permission.PermissionResponse;
import com.m3pro.groundflip.service.PermissionService;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@RestController
@RequiredArgsConstructor
@Slf4j
@RequestMapping("/api/permission")
@Tag(name = "permission", description = "권한 동의 API")
@SecurityRequirement(name = "Authorization")
public class PermissionController {
private final PermissionService permissionService;

@Operation(summary = "유저가 동의한 권한 조회", description = "유저가 동의한 권한을 조회하여 응답한다.")
@GetMapping("/{userId}")
public Response<PermissionResponse> getPermission(@PathVariable("userId") Long userId) {
return Response.createSuccess(permissionService.getAllPermissions(userId));
}

@Operation(summary = "서비스 알림 권한 동의", description = "서비스 푸시 알림을 받을지 받지 않을지 선택하는 api 이다.")
@PutMapping("/service-notification")
public Response<?> updateServiceNotification(@RequestBody PermissionRequest permissionRequest) {
permissionService.updateServiceNotificationsPreference(permissionRequest);
return Response.createSuccessWithNoData();
}

@Operation(summary = "마케팅 알림 권한 동의", description = "마케팅 푸시 알림을 받을지 받지 않을지 선택하는 api 이다.")
@PutMapping("/marketing-notification")
public Response<?> updateMarketingNotification(@RequestBody PermissionRequest permissionRequest) {
permissionService.updateMarketingNotificationsPreference(permissionRequest);
return Response.createSuccessWithNoData();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.m3pro.groundflip.domain.dto.permission;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@AllArgsConstructor
@NoArgsConstructor
@Data
@Builder
@Schema(title = "권한 동의 Body")
public class PermissionRequest {
@Schema(description = "사용자 ID", example = "5")
private Long userId;

@Schema(description = "권한 동의 여부", example = "true")
private boolean isEnabled;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.m3pro.groundflip.domain.dto.permission;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@AllArgsConstructor
@NoArgsConstructor
@Data
@Builder
@Schema(title = "권한 정보 body")
public class PermissionResponse {
@Schema(description = "서비스 알림 동의 여부", example = "true")
private boolean isServiceNotificationEnabled;

@Schema(description = "마케팅 알림 동의 여부", example = "true")
private boolean isMarketingNotificationEnabled;
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,8 @@ public class FcmToken extends BaseTimeEntity {
@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id")
private User user;

public void updateToken(String token) {
this.token = token;
}
}
49 changes: 49 additions & 0 deletions src/main/java/com/m3pro/groundflip/domain/entity/Permission.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package com.m3pro.groundflip.domain.entity;

import com.m3pro.groundflip.domain.entity.global.BaseTimeEntity;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
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 lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@Entity
@Table(name = "permission")
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
@Builder
public class Permission extends BaseTimeEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "permission_id")
private Long id;

@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id", nullable = false)
private User user;

@Column(nullable = false)
private Boolean serviceNotificationsEnabled;

@Column(nullable = false)
private Boolean marketingNotificationsEnabled;

public void updateServiceNotificationsEnabled(boolean enabled) {
serviceNotificationsEnabled = enabled;
}

public void updateMarketingNotificationsEnabled(boolean enabled) {
marketingNotificationsEnabled = enabled;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.m3pro.groundflip.repository;

import java.util.Optional;

import org.springframework.data.jpa.repository.JpaRepository;

import com.m3pro.groundflip.domain.entity.Permission;
import com.m3pro.groundflip.domain.entity.User;

public interface PermissionRepository extends JpaRepository<Permission, Long> {
Optional<Permission> findByUser(User user);
}
14 changes: 11 additions & 3 deletions src/main/java/com/m3pro/groundflip/service/AuthService.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,13 @@
import com.m3pro.groundflip.domain.dto.auth.OauthUserInfoResponse;
import com.m3pro.groundflip.domain.dto.auth.ReissueReponse;
import com.m3pro.groundflip.domain.entity.AppleRefreshToken;
import com.m3pro.groundflip.domain.entity.Permission;
import com.m3pro.groundflip.domain.entity.User;
import com.m3pro.groundflip.enums.Provider;
import com.m3pro.groundflip.enums.UserStatus;
import com.m3pro.groundflip.jwt.JwtProvider;
import com.m3pro.groundflip.repository.AppleRefreshTokenRepository;
import com.m3pro.groundflip.repository.PermissionRepository;
import com.m3pro.groundflip.repository.UserRepository;
import com.m3pro.groundflip.service.oauth.OauthService;

Expand All @@ -29,6 +31,7 @@ public class AuthService {
private final JwtProvider jwtProvider;
private final UserRepository userRepository;
private final AppleRefreshTokenRepository appleRefreshTokenRepository;
private final PermissionRepository permissionRepository;

/**
* Oauth Provider를 사용해 로그인을 진행한다.
Expand Down Expand Up @@ -68,12 +71,17 @@ public LoginResponse login(Provider provider, LoginRequest loginRequest) {
* @author 김민욱
*/
private User registerUser(Provider provider, String email) {
User newUser = User.builder()
User newUser = userRepository.save(User.builder()
.email(email)
.provider(provider)
.status(UserStatus.PENDING)
.build();
return userRepository.save(newUser);
.build());
permissionRepository.save(Permission.builder()
.user(newUser)
.serviceNotificationsEnabled(false)
.marketingNotificationsEnabled(false)
.build());
return newUser;
}

/**
Expand Down
1 change: 1 addition & 0 deletions src/main/java/com/m3pro/groundflip/service/FcmService.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ public void registerFcmToken(FcmTokenRequest fcmTokenRequest) {

if (fcmToken.isPresent()) {
fcmToken.get().updateModifiedAtToNow();
fcmToken.get().updateToken(fcmTokenRequest.getFcmToken());
} else {
fcmTokenRepository.save(
FcmToken.builder()
Expand Down
52 changes: 52 additions & 0 deletions src/main/java/com/m3pro/groundflip/service/PermissionService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package com.m3pro.groundflip.service;

import org.springframework.stereotype.Service;

import com.m3pro.groundflip.domain.dto.permission.PermissionRequest;
import com.m3pro.groundflip.domain.dto.permission.PermissionResponse;
import com.m3pro.groundflip.domain.entity.Permission;
import com.m3pro.groundflip.domain.entity.User;
import com.m3pro.groundflip.exception.AppException;
import com.m3pro.groundflip.exception.ErrorCode;
import com.m3pro.groundflip.repository.PermissionRepository;
import com.m3pro.groundflip.repository.UserRepository;

import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Service
@RequiredArgsConstructor
@Slf4j
public class PermissionService {
private final UserRepository userRepository;
private final PermissionRepository permissionRepository;

public PermissionResponse getAllPermissions(Long userId) {
User user = userRepository.getReferenceById(userId);
Permission permission = permissionRepository.findByUser(user)
.orElseThrow(() -> new AppException(ErrorCode.USER_NOT_FOUND));
return new PermissionResponse(
permission.getServiceNotificationsEnabled(),
permission.getMarketingNotificationsEnabled()
);
}

@Transactional
public void updateServiceNotificationsPreference(PermissionRequest permissionRequest) {
User user = userRepository.findById(permissionRequest.getUserId()).orElseThrow(() -> new AppException(
ErrorCode.USER_NOT_FOUND));
Permission permission = permissionRepository.findByUser(user)
.orElseThrow(() -> new AppException(ErrorCode.INTERNAL_SERVER_ERROR));
permission.updateServiceNotificationsEnabled(permissionRequest.isEnabled());
}

@Transactional
public void updateMarketingNotificationsPreference(PermissionRequest permissionRequest) {
User user = userRepository.findById(permissionRequest.getUserId()).orElseThrow(() -> new AppException(
ErrorCode.USER_NOT_FOUND));
Permission permission = permissionRepository.findByUser(user)
.orElseThrow(() -> new AppException(ErrorCode.INTERNAL_SERVER_ERROR));
permission.updateMarketingNotificationsEnabled(permissionRequest.isEnabled());
}
}
6 changes: 6 additions & 0 deletions src/main/resources/static/permission_insert.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
INSERT INTO permission (user_id, marketing_notifications_enabled, service_notifications_enabled, created_at,
modified_at)
SELECT u.user_id, FALSE, FALSE, NOW(), NOW()
FROM user u
LEFT JOIN permission p ON u.user_id = p.user_id
WHERE p.user_id IS NULL;
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import com.m3pro.groundflip.enums.UserStatus;
import com.m3pro.groundflip.jwt.JwtProvider;
import com.m3pro.groundflip.repository.AppleRefreshTokenRepository;
import com.m3pro.groundflip.repository.PermissionRepository;
import com.m3pro.groundflip.repository.RankingRedisRepository;
import com.m3pro.groundflip.repository.UserRepository;
import com.m3pro.groundflip.service.oauth.OauthService;
Expand All @@ -41,6 +42,8 @@ class AuthServiceTest {
private RankingRedisRepository rankingRedisRepository;
@Mock
private AppleRefreshTokenRepository appleRefreshTokenRepository;
@Mock
private PermissionRepository permissionRepository;
@InjectMocks
private AuthService authService;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package com.m3pro.groundflip.service;

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;

import java.util.Optional;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import com.m3pro.groundflip.domain.dto.permission.PermissionRequest;
import com.m3pro.groundflip.domain.dto.permission.PermissionResponse;
import com.m3pro.groundflip.domain.entity.Permission;
import com.m3pro.groundflip.domain.entity.User;
import com.m3pro.groundflip.repository.PermissionRepository;
import com.m3pro.groundflip.repository.UserRepository;

@ExtendWith(MockitoExtension.class)
class PermissionServiceTest {
@Mock
private UserRepository userRepository;

@Mock
private PermissionRepository permissionRepository;

@InjectMocks
private PermissionService permissionService;

private User mockUser;
private Permission mockPermission;

@BeforeEach
void setUp() {
mockUser = User.builder()
.id(1L)
.nickname("testUser")
.build();

mockPermission = Permission.builder()
.serviceNotificationsEnabled(true)
.marketingNotificationsEnabled(true)
.user(mockUser)
.build();
}

@Test
@DisplayName("[getAllPermissions] userId에 해당하는 권한의 정보를 가져온다.")
void testGetAllPermissions() {
when(userRepository.getReferenceById(anyLong())).thenReturn(mockUser);
when(permissionRepository.findByUser(any(User.class))).thenReturn(Optional.of(mockPermission));

PermissionResponse response = permissionService.getAllPermissions(1L);

assertTrue(response.isServiceNotificationEnabled());
assertTrue(response.isMarketingNotificationEnabled());

verify(userRepository, times(1)).getReferenceById(1L);
verify(permissionRepository, times(1)).findByUser(mockUser);
}

@Test
@DisplayName("[updateServiceNotificationsPreference] service 권한을 정상적으로 변경한다.")
void testUpdateServiceNotificationsPreference() {
PermissionRequest request = new PermissionRequest(1L, false);

when(userRepository.findById(anyLong())).thenReturn(Optional.of(mockUser));
when(permissionRepository.findByUser(any(User.class))).thenReturn(Optional.of(mockPermission));

permissionService.updateServiceNotificationsPreference(request);

verify(permissionRepository, times(1)).findByUser(mockUser);
assertEquals(false, mockPermission.getServiceNotificationsEnabled());
}

@Test
@DisplayName("[updateMarketingNotificationsPreference] marketing 권한을 정상적으로 변경한다.")
void testUpdateMarketingNotificationsPreference() {
PermissionRequest request = new PermissionRequest(1L, false);

when(userRepository.findById(anyLong())).thenReturn(Optional.of(mockUser));
when(permissionRepository.findByUser(any(User.class))).thenReturn(Optional.of(mockPermission));

permissionService.updateMarketingNotificationsPreference(request);

verify(permissionRepository, times(1)).findByUser(mockUser);
assertEquals(false, mockPermission.getMarketingNotificationsEnabled());
}
}
Loading