Skip to content

Commit

Permalink
Merge pull request #147 from Team-Ampersand/144-withdrawal-impl
Browse files Browse the repository at this point in the history
🔀 :: [#144] 회원탈퇴 기능 추가
  • Loading branch information
baekteun committed Aug 2, 2023
2 parents 422a7a7 + d8a9af0 commit df446ab
Show file tree
Hide file tree
Showing 24 changed files with 191 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import Foundation
*/
public enum DotoriRestAPIDomain: String {
case auth
case user = "members"
case selfStudy = "dotori-role/self-study"
case massage = "dotori-role/massage"
case notice = "dotori-role/board"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import AuthDomainInterface
import Emdpoint
import NetworkingInterface

public enum AuthEndpoint {
case signin(email: String, password: String)
Expand Down Expand Up @@ -42,4 +44,18 @@ extension AuthEndpoint: DotoriEndpoint {
return .none
}
}

public var errorMap: [Int: Error] {
switch self {
case .signin:
return [
400: AuthDomainError.invalidPassword,
409: AuthDomainError.invalidPassword,
500: AuthDomainError.unknown
]

default:
return [:]
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
public protocol RemoteUserDataSource {
func withdrawal() async throws
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ import Foundation
public protocol UserRepository {
func loadCurrentUserRole() throws -> UserRoleType
func logout()
func withdrawal() async throws
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
public protocol WithdrawalUseCase {
func callAsFunction() async throws
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import JwtStoreInterface
import KeyValueStoreInterface
import NetworkingInterface
import Swinject
import UserDomainInterface

Expand All @@ -14,8 +15,15 @@ public final class UserDomainAssembly: Assembly {
}
.inObjectScope(.container)

container.register(RemoteUserDataSource.self) { resolver in
RemoteUserDataSourceImpl(networking: resolver.resolve(Networking.self)!)
}

container.register(UserRepository.self) { resolver in
UserRepositoryImpl(localUserDataSource: resolver.resolve(LocalUserDataSource.self)!)
UserRepositoryImpl(
localUserDataSource: resolver.resolve(LocalUserDataSource.self)!,
remoteUserDataSource: resolver.resolve(RemoteUserDataSource.self)!
)
}
.inObjectScope(.container)

Expand All @@ -26,5 +34,9 @@ public final class UserDomainAssembly: Assembly {
container.register(LogoutUseCase.self) { resolver in
LogoutUseCaseImpl(userRepository: resolver.resolve(UserRepository.self)!)
}

container.register(WithdrawalUseCase.self) { resolver in
WithdrawalUseCaseImpl(userRepository: resolver.resolve(UserRepository.self)!)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import UserDomainInterface
import NetworkingInterface

final class RemoteUserDataSourceImpl: RemoteUserDataSource {
private let networking: any Networking

init(networking: any Networking) {
self.networking = networking
}

func withdrawal() async throws {
try await networking.request(UserEndpoint.withdrawal)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import Emdpoint
import NetworkingInterface

public enum UserEndpoint {
case withdrawal
}

extension UserEndpoint: DotoriEndpoint {
public var domain: DotoriRestAPIDomain {
.user
}

public var route: Route {
switch self {
case .withdrawal:
return .delete("/withdrawal")
}
}

public var task: HTTPTask {
switch self {
default:
return .requestPlain
}
}

public var jwtTokenType: JwtTokenType {
switch self {
default:
return .accessToken
}
}

public var errorMap: [Int: Error] {
switch self {
default:
return [:]
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,14 @@ import UserDomainInterface

final class UserRepositoryImpl: UserRepository {
private let localUserDataSource: any LocalUserDataSource
private let remoteUserDataSource: any RemoteUserDataSource

init(localUserDataSource: any LocalUserDataSource) {
init(
localUserDataSource: any LocalUserDataSource,
remoteUserDataSource: any RemoteUserDataSource
) {
self.localUserDataSource = localUserDataSource
self.remoteUserDataSource = remoteUserDataSource
}

func loadCurrentUserRole() throws -> UserRoleType {
Expand All @@ -16,4 +21,8 @@ final class UserRepositoryImpl: UserRepository {
func logout() {
localUserDataSource.logout()
}

func withdrawal() async throws {
try await remoteUserDataSource.withdrawal()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import UserDomainInterface

struct WithdrawalUseCaseImpl: WithdrawalUseCase {
private let userRepository: any UserRepository

init(userRepository: any UserRepository) {
self.userRepository = userRepository
}

func callAsFunction() async throws {
try await userRepository.withdrawal()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import Foundation
import UserDomainInterface

final class RemoteUserDataSourceSpy: RemoteUserDataSource {
var withdrawalCallCount = 0
func withdrawal() async throws {
withdrawalCallCount += 1
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,9 @@ final class UserRepositorySpy: UserRepository {
func logout() {
logoutCallCount += 1
}

var withdrawalCallCount = 0
func withdrawal() async throws {
withdrawalCallCount += 1
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import UserDomainInterface

final class WithdrawalUseCaseSpy: WithdrawalUseCase {
var withdrawalCallCount = 0
func callAsFunction() async throws {
withdrawalCallCount += 1
}
}
8 changes: 7 additions & 1 deletion Projects/Domain/UserDomain/Tests/UserRepositoryTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,23 @@ import XCTest

final class UserRepositoryTests: XCTestCase {
var userRepository: UserRepositoryImpl!
var remoteUserDataSourceSpy: RemoteUserDataSourceSpy!
var localUserDataSourceSpy: LocalUserDataSourceSpy!

override func setUp() {
super.setUp()
remoteUserDataSourceSpy = .init()
localUserDataSourceSpy = LocalUserDataSourceSpy()
userRepository = UserRepositoryImpl(localUserDataSource: localUserDataSourceSpy)
userRepository = UserRepositoryImpl(
localUserDataSource: localUserDataSourceSpy,
remoteUserDataSource: remoteUserDataSourceSpy
)
}

override func tearDown() {
userRepository = nil
localUserDataSourceSpy = nil
remoteUserDataSourceSpy = nil
super.tearDown()
}

Expand Down
4 changes: 3 additions & 1 deletion Projects/Feature/HomeFeature/Demo/Sources/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ final class AppDelegate: UIResponder, UIApplicationDelegate {
let cancelMassageUseCase = CancelMassageUseCaseSpy()
let modifyMassagePersonnelUseCase = ModifyMassagePersonnelUseCaseSpy()
let logoutUseCase = LogoutUseCaseSpy()
let withdrawalUseCase = WithdrawalUseCaseSpy()
let store = HomeStore(
repeatableTimer: repeatableTimerStub,
fetchSelfStudyInfoUseCase: fetchSelfStudyInfoUseCase,
Expand All @@ -46,7 +47,8 @@ final class AppDelegate: UIResponder, UIApplicationDelegate {
applyMassageUseCase: applyMassageUseCase,
cancelMassageUseCase: cancelMassageUseCase,
modifyMassagePersonnelUseCase: modifyMassagePersonnelUseCase,
logoutUseCase: logoutUseCase
logoutUseCase: logoutUseCase,
withdrawalUseCase: withdrawalUseCase
)
let viewController = Inject.ViewControllerHost(
UINavigationController(rootViewController: HomeViewController(store: store))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ public final class HomeAssembly: Assembly {
cancelMassageUseCase: resolver.resolve(CancelMassageUseCase.self)!,
modifyMassagePersonnelUseCase: resolver.resolve(ModifyMassagePersonnelUseCase.self)!,
logoutUseCase: resolver.resolve(LogoutUseCase.self)!,
withdrawalUseCase: resolver.resolve(WithdrawalUseCase.self)!,
confirmationDialogFactory: resolver.resolve(ConfirmationDialogFactory.self)!,
myViolationListFactory: resolver.resolve(MyViolationListFactory.self)!,
inputDialogFactory: resolver.resolve(InputDialogFactory.self)!
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ struct HomeFactoryImpl: HomeFactory {
private let cancelMassageUseCase: any CancelMassageUseCase
private let modifyMassagePersonnelUseCase: any ModifyMassagePersonnelUseCase
private let logoutUseCase: any LogoutUseCase
private let withdrawalUseCase: any WithdrawalUseCase
private let confirmationDialogFactory: any ConfirmationDialogFactory
private let myViolationListFactory: any MyViolationListFactory
private let inputDialogFactory: any InputDialogFactory
Expand All @@ -38,6 +39,7 @@ struct HomeFactoryImpl: HomeFactory {
cancelMassageUseCase: any CancelMassageUseCase,
modifyMassagePersonnelUseCase: any ModifyMassagePersonnelUseCase,
logoutUseCase: any LogoutUseCase,
withdrawalUseCase: any WithdrawalUseCase,
confirmationDialogFactory: any ConfirmationDialogFactory,
myViolationListFactory: any MyViolationListFactory,
inputDialogFactory: any InputDialogFactory
Expand All @@ -54,6 +56,7 @@ struct HomeFactoryImpl: HomeFactory {
self.cancelMassageUseCase = cancelMassageUseCase
self.modifyMassagePersonnelUseCase = modifyMassagePersonnelUseCase
self.logoutUseCase = logoutUseCase
self.withdrawalUseCase = withdrawalUseCase
self.confirmationDialogFactory = confirmationDialogFactory
self.myViolationListFactory = myViolationListFactory
self.inputDialogFactory = inputDialogFactory
Expand All @@ -72,7 +75,8 @@ struct HomeFactoryImpl: HomeFactory {
applyMassageUseCase: applyMassageUseCase,
cancelMassageUseCase: cancelMassageUseCase,
modifyMassagePersonnelUseCase: modifyMassagePersonnelUseCase,
logoutUseCase: logoutUseCase
logoutUseCase: logoutUseCase,
withdrawalUseCase: withdrawalUseCase
)
let homeViewController = HomeViewController(store: homeStore)
return HomeMoordinator(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ final class HomeViewController: BaseStoredViewController<HomeStore> {
maxApplyCount: 5
)
private let mealCardView = MealCardView()
private let bottomSpacerView = SpacerView(height: 40)
.set(\.backgroundColor, .clear.withAlphaComponent(0.0125))

override func setLayout() {
MSGLayout.stackedScrollLayout(view) {
Expand All @@ -41,7 +43,7 @@ final class HomeViewController: BaseStoredViewController<HomeStore> {

mealCardView

SpacerView(height: 32)
bottomSpacerView
}
.margin(.horizontal(20))
}
Expand Down Expand Up @@ -108,6 +110,15 @@ final class HomeViewController: BaseStoredViewController<HomeStore> {
.map { Store.Action.refreshMassageButtonDidTap }
.sink(receiveValue: store.send(_:))
.store(in: &subscription)

bottomSpacerView.tapGesturePublisher()
.sink { _ in
guard let url = URL(
string: "https://apps.apple.com/kr/app/%EC%98%A4%EB%8A%98-%EB%AD%90%EC%9E%84/id1629567018"
) else { return }
UIApplication.shared.open(url)
}
.store(in: &subscription)
}

override func bindState() {
Expand Down
19 changes: 18 additions & 1 deletion Projects/Feature/HomeFeature/Sources/Scene/Store/HomeStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ final class HomeStore: BaseStore {
private let cancelMassageUseCase: any CancelMassageUseCase
private let modifyMassagePersonnelUseCase: any ModifyMassagePersonnelUseCase
private let logoutUseCase: any LogoutUseCase
private let withdrawalUseCase: any WithdrawalUseCase

init(
repeatableTimer: any RepeatableTimer,
Expand All @@ -48,7 +49,8 @@ final class HomeStore: BaseStore {
applyMassageUseCase: any ApplyMassageUseCase,
cancelMassageUseCase: any CancelMassageUseCase,
modifyMassagePersonnelUseCase: any ModifyMassagePersonnelUseCase,
logoutUseCase: any LogoutUseCase
logoutUseCase: any LogoutUseCase,
withdrawalUseCase: any WithdrawalUseCase
) {
self.initialState = .init()
self.stateSubject = .init(initialState)
Expand All @@ -64,6 +66,7 @@ final class HomeStore: BaseStore {
self.cancelMassageUseCase = cancelMassageUseCase
self.modifyMassagePersonnelUseCase = modifyMassagePersonnelUseCase
self.logoutUseCase = logoutUseCase
self.withdrawalUseCase = withdrawalUseCase
}

enum Action: Equatable {
Expand Down Expand Up @@ -177,6 +180,20 @@ private extension HomeStore {
}
route.send(confirmationDialogRoutePath)
},
.init(title: L10n.Home.withdrawalTitle, style: .destructive, handler: { [route, withdrawalUseCase] _ in
let confirmationDialogRoutePath = DotoriRoutePath.confirmationDialog(
title: L10n.Home.withdrawalTitle,
description: L10n.Home.reallyWithdrawalTitle
) {
do {
try await withdrawalUseCase()
route.send(DotoriRoutePath.signin)
} catch {
await DotoriToast.makeToast(text: error.localizedDescription, style: .error)
}
}
route.send(confirmationDialogRoutePath)
}),
.init(title: L10n.Global.cancelButtonTitle, style: .cancel)
])
self.route.send(alertPath)
Expand Down
Loading

0 comments on commit df446ab

Please sign in to comment.