From f90a6cf8e16931a0e2960b442ff56ce68b743196 Mon Sep 17 00:00:00 2001 From: baegteun Date: Wed, 2 Aug 2023 21:20:11 +0900 Subject: [PATCH 1/6] :sparkles: :: [#144] AuthDomain / WithdrawalUseCase Impl --- .../Interface/DataSource/RemoteAuthDataSource.swift | 1 + .../Interface/Repository/AuthRepository.swift | 1 + .../Interface/UseCase/WithdrawalUseCase.swift | 3 +++ .../Sources/DataSource/Remote}/AuthEndpoint.swift | 5 +++++ .../{ => Remote}/RemoteAuthDataSourceImpl.swift | 4 ++++ .../Sources/Repository/AuthRepositoryImpl.swift | 4 ++++ .../Sources/UseCase/WithdrawalUseCaseImpl.swift | 13 +++++++++++++ .../Testing/Repository/AuthRepositorySpy.swift | 5 +++++ .../Testing/UseCase/WithdrawalUseCaseSpy.swift | 8 ++++++++ 9 files changed, 44 insertions(+) create mode 100644 Projects/Domain/AuthDomain/Interface/UseCase/WithdrawalUseCase.swift rename Projects/{Core/Networking/Interface/Auth => Domain/AuthDomain/Sources/DataSource/Remote}/AuthEndpoint.swift (88%) rename Projects/Domain/AuthDomain/Sources/DataSource/{ => Remote}/RemoteAuthDataSourceImpl.swift (92%) create mode 100644 Projects/Domain/AuthDomain/Sources/UseCase/WithdrawalUseCaseImpl.swift create mode 100644 Projects/Domain/AuthDomain/Testing/UseCase/WithdrawalUseCaseSpy.swift diff --git a/Projects/Domain/AuthDomain/Interface/DataSource/RemoteAuthDataSource.swift b/Projects/Domain/AuthDomain/Interface/DataSource/RemoteAuthDataSource.swift index 98688c8b..179e0c10 100644 --- a/Projects/Domain/AuthDomain/Interface/DataSource/RemoteAuthDataSource.swift +++ b/Projects/Domain/AuthDomain/Interface/DataSource/RemoteAuthDataSource.swift @@ -5,4 +5,5 @@ public protocol RemoteAuthDataSource { func signin(req: SigninRequestDTO) async throws func tokenRefresh() async throws func networkIsConnected() async -> Bool + func withdrawal() async throws } diff --git a/Projects/Domain/AuthDomain/Interface/Repository/AuthRepository.swift b/Projects/Domain/AuthDomain/Interface/Repository/AuthRepository.swift index f75f1584..79cd4d48 100644 --- a/Projects/Domain/AuthDomain/Interface/Repository/AuthRepository.swift +++ b/Projects/Domain/AuthDomain/Interface/Repository/AuthRepository.swift @@ -7,4 +7,5 @@ public protocol AuthRepository { func tokenRefresh() async throws func networkIsConnected() async -> Bool func checkTokenIsExist() -> Bool + func withdrawal() async throws } diff --git a/Projects/Domain/AuthDomain/Interface/UseCase/WithdrawalUseCase.swift b/Projects/Domain/AuthDomain/Interface/UseCase/WithdrawalUseCase.swift new file mode 100644 index 00000000..cfae3ce4 --- /dev/null +++ b/Projects/Domain/AuthDomain/Interface/UseCase/WithdrawalUseCase.swift @@ -0,0 +1,3 @@ +public protocol WithdrawalUseCase { + func callAsFunction() async throws +} diff --git a/Projects/Core/Networking/Interface/Auth/AuthEndpoint.swift b/Projects/Domain/AuthDomain/Sources/DataSource/Remote/AuthEndpoint.swift similarity index 88% rename from Projects/Core/Networking/Interface/Auth/AuthEndpoint.swift rename to Projects/Domain/AuthDomain/Sources/DataSource/Remote/AuthEndpoint.swift index 7e29b74d..255deb74 100644 --- a/Projects/Core/Networking/Interface/Auth/AuthEndpoint.swift +++ b/Projects/Domain/AuthDomain/Sources/DataSource/Remote/AuthEndpoint.swift @@ -1,8 +1,10 @@ import Emdpoint +import NetworkingInterface public enum AuthEndpoint { case signin(email: String, password: String) case refresh + case withdrawal } extension AuthEndpoint: DotoriEndpoint { @@ -17,6 +19,9 @@ extension AuthEndpoint: DotoriEndpoint { case .refresh: return .patch("") + + case .withdrawal: + return .post("/withdrawal") } } diff --git a/Projects/Domain/AuthDomain/Sources/DataSource/RemoteAuthDataSourceImpl.swift b/Projects/Domain/AuthDomain/Sources/DataSource/Remote/RemoteAuthDataSourceImpl.swift similarity index 92% rename from Projects/Domain/AuthDomain/Sources/DataSource/RemoteAuthDataSourceImpl.swift rename to Projects/Domain/AuthDomain/Sources/DataSource/Remote/RemoteAuthDataSourceImpl.swift index 80bae8ee..a91ad598 100644 --- a/Projects/Domain/AuthDomain/Sources/DataSource/RemoteAuthDataSourceImpl.swift +++ b/Projects/Domain/AuthDomain/Sources/DataSource/Remote/RemoteAuthDataSourceImpl.swift @@ -38,4 +38,8 @@ final class RemoteAuthDataSourceImpl: RemoteAuthDataSource { monitor.start(queue: DispatchQueue(label: "InternetConnectionMonitor")) }) } + + func withdrawal() async throws { + try await networking.request(AuthEndpoint.withdrawal) + } } diff --git a/Projects/Domain/AuthDomain/Sources/Repository/AuthRepositoryImpl.swift b/Projects/Domain/AuthDomain/Sources/Repository/AuthRepositoryImpl.swift index 4ec220ef..c56403fc 100644 --- a/Projects/Domain/AuthDomain/Sources/Repository/AuthRepositoryImpl.swift +++ b/Projects/Domain/AuthDomain/Sources/Repository/AuthRepositoryImpl.swift @@ -32,4 +32,8 @@ final class AuthRepositoryImpl: AuthRepository { func networkIsConnected() async -> Bool { await remoteAuthDataSource.networkIsConnected() } + + func withdrawal() async throws { + try await remoteAuthDataSource.withdrawal() + } } diff --git a/Projects/Domain/AuthDomain/Sources/UseCase/WithdrawalUseCaseImpl.swift b/Projects/Domain/AuthDomain/Sources/UseCase/WithdrawalUseCaseImpl.swift new file mode 100644 index 00000000..418c2030 --- /dev/null +++ b/Projects/Domain/AuthDomain/Sources/UseCase/WithdrawalUseCaseImpl.swift @@ -0,0 +1,13 @@ +import AuthDomainInterface + +struct WithdrawalUseCaseImpl: WithdrawalUseCase { + private let authRepository: any AuthRepository + + init(authRepository: any AuthRepository) { + self.authRepository = authRepository + } + + func callAsFunction() async throws { + try await authRepository.withdrawal() + } +} diff --git a/Projects/Domain/AuthDomain/Testing/Repository/AuthRepositorySpy.swift b/Projects/Domain/AuthDomain/Testing/Repository/AuthRepositorySpy.swift index e650e707..72dd68da 100644 --- a/Projects/Domain/AuthDomain/Testing/Repository/AuthRepositorySpy.swift +++ b/Projects/Domain/AuthDomain/Testing/Repository/AuthRepositorySpy.swift @@ -43,4 +43,9 @@ final class AuthRepositorySpy: AuthRepository { checkTokenIsExistCallCount += 1 return checkTokenIsExistReturn } + + var withdrawalCallCount = 0 + func withdrawal() async throws { + withdrawalCallCount += 1 + } } diff --git a/Projects/Domain/AuthDomain/Testing/UseCase/WithdrawalUseCaseSpy.swift b/Projects/Domain/AuthDomain/Testing/UseCase/WithdrawalUseCaseSpy.swift new file mode 100644 index 00000000..76ce75f3 --- /dev/null +++ b/Projects/Domain/AuthDomain/Testing/UseCase/WithdrawalUseCaseSpy.swift @@ -0,0 +1,8 @@ +import AuthDomainInterface + +final class WithdrawalUseCaseSpy: WithdrawalUseCase { + var withdrawalCallCount = 0 + func callAsFunction() async throws { + withdrawalCallCount += 1 + } +} From 92854b56f1193977f39286b1370f6f5620b83ed1 Mon Sep 17 00:00:00 2001 From: baegteun Date: Wed, 2 Aug 2023 21:21:31 +0900 Subject: [PATCH 2/6] :sparkles: :: [#144] AuthDomain / WithdrawalUseCase DI --- .../AuthDomain/Sources/Assembly/AuthDomainAssembly.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Projects/Domain/AuthDomain/Sources/Assembly/AuthDomainAssembly.swift b/Projects/Domain/AuthDomain/Sources/Assembly/AuthDomainAssembly.swift index 4b2e0666..7c69b454 100644 --- a/Projects/Domain/AuthDomain/Sources/Assembly/AuthDomainAssembly.swift +++ b/Projects/Domain/AuthDomain/Sources/Assembly/AuthDomainAssembly.swift @@ -42,5 +42,9 @@ public final class AuthDomainAssembly: Assembly { container.register(CheckIsLoggedInUseCase.self) { resolver in CheckIsLoggedInUseCaseImpl(authRepository: resolver.resolve(AuthRepository.self)!) } + + container.register(WithdrawalUseCase.self) { resolver in + WithdrawalUseCaseImpl(authRepository: resolver.resolve(AuthRepository.self)!) + } } } From 2add4ee4d2037c90fa10e5bf347352c655bbd1d8 Mon Sep 17 00:00:00 2001 From: baegteun Date: Wed, 2 Aug 2023 21:58:42 +0900 Subject: [PATCH 3/6] =?UTF-8?q?:pencil2:=20::=20[#144]=20UserDomain=20/=20?= =?UTF-8?q?AuthDomain=EB=A7=90=EA=B3=A0=20UserDomain=EC=97=90=20=ED=9A=8C?= =?UTF-8?q?=EC=9B=90=ED=83=88=ED=87=B4=EA=B0=80=20=EC=9E=88=EC=97=88?= =?UTF-8?q?=EC=8A=B5=EB=8B=88=EB=8B=A4=3F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Endpoint/DotoriRestAPIDomain.swift | 1 + .../DataSource/RemoteAuthDataSource.swift | 1 - .../Interface/Repository/AuthRepository.swift | 1 - .../Sources/Assembly/AuthDomainAssembly.swift | 4 -- .../DataSource/Remote/AuthEndpoint.swift | 19 +++++++-- .../Remote/RemoteAuthDataSourceImpl.swift | 4 -- .../Repository/AuthRepositoryImpl.swift | 4 -- .../UseCase/WithdrawalUseCaseImpl.swift | 13 ------ .../Repository/AuthRepositorySpy.swift | 5 --- .../DataSource/RemoteUserDataSource.swift | 3 ++ .../Interface/Repository/UserRepository.swift | 1 + .../Interface/UseCase/WithdrawalUseCase.swift | 0 .../Sources/Assembly/UserDomainAssembly.swift | 14 ++++++- .../{ => Local}/LocalUserDataSourceImpl.swift | 0 .../Remote/RemoteUserDataSourceImpl.swift | 14 +++++++ .../DataSource/Remote/UserEndpoint.swift | 41 +++++++++++++++++++ .../Repository/UserRepositoryImpl.swift | 11 ++++- .../UseCase/WithdrawalUseCaseImpl.swift | 13 ++++++ .../DataSource/RemoteUserDataSourceSpy.swift | 9 ++++ .../Repository/UserRepositorySpy.swift | 5 +++ .../UseCase/WithdrawalUseCaseSpy.swift | 2 +- .../UserDomain/Tests/UserRepositoryTest.swift | 8 +++- .../Sources/Assembly/HomeAssembly.swift | 1 + .../Sources/Factory/HomeFactoryImpl.swift | 6 ++- .../Sources/Scene/Store/HomeStore.swift | 19 ++++++++- .../HomeFeature/Tests/HomeFeatureTest.swift | 7 +++- .../Resources/en.lproj/Home.strings | 2 + .../Resources/ko.lproj/Home.strings | 2 + 28 files changed, 167 insertions(+), 43 deletions(-) delete mode 100644 Projects/Domain/AuthDomain/Sources/UseCase/WithdrawalUseCaseImpl.swift create mode 100644 Projects/Domain/UserDomain/Interface/DataSource/RemoteUserDataSource.swift rename Projects/Domain/{AuthDomain => UserDomain}/Interface/UseCase/WithdrawalUseCase.swift (100%) rename Projects/Domain/UserDomain/Sources/DataSource/{ => Local}/LocalUserDataSourceImpl.swift (100%) create mode 100644 Projects/Domain/UserDomain/Sources/DataSource/Remote/RemoteUserDataSourceImpl.swift create mode 100644 Projects/Domain/UserDomain/Sources/DataSource/Remote/UserEndpoint.swift create mode 100644 Projects/Domain/UserDomain/Sources/UseCase/WithdrawalUseCaseImpl.swift create mode 100644 Projects/Domain/UserDomain/Testing/DataSource/RemoteUserDataSourceSpy.swift rename Projects/Domain/{AuthDomain => UserDomain}/Testing/UseCase/WithdrawalUseCaseSpy.swift (86%) diff --git a/Projects/Core/Networking/Interface/Endpoint/DotoriRestAPIDomain.swift b/Projects/Core/Networking/Interface/Endpoint/DotoriRestAPIDomain.swift index 5a42fc01..bd8f7797 100644 --- a/Projects/Core/Networking/Interface/Endpoint/DotoriRestAPIDomain.swift +++ b/Projects/Core/Networking/Interface/Endpoint/DotoriRestAPIDomain.swift @@ -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" diff --git a/Projects/Domain/AuthDomain/Interface/DataSource/RemoteAuthDataSource.swift b/Projects/Domain/AuthDomain/Interface/DataSource/RemoteAuthDataSource.swift index 179e0c10..98688c8b 100644 --- a/Projects/Domain/AuthDomain/Interface/DataSource/RemoteAuthDataSource.swift +++ b/Projects/Domain/AuthDomain/Interface/DataSource/RemoteAuthDataSource.swift @@ -5,5 +5,4 @@ public protocol RemoteAuthDataSource { func signin(req: SigninRequestDTO) async throws func tokenRefresh() async throws func networkIsConnected() async -> Bool - func withdrawal() async throws } diff --git a/Projects/Domain/AuthDomain/Interface/Repository/AuthRepository.swift b/Projects/Domain/AuthDomain/Interface/Repository/AuthRepository.swift index 79cd4d48..f75f1584 100644 --- a/Projects/Domain/AuthDomain/Interface/Repository/AuthRepository.swift +++ b/Projects/Domain/AuthDomain/Interface/Repository/AuthRepository.swift @@ -7,5 +7,4 @@ public protocol AuthRepository { func tokenRefresh() async throws func networkIsConnected() async -> Bool func checkTokenIsExist() -> Bool - func withdrawal() async throws } diff --git a/Projects/Domain/AuthDomain/Sources/Assembly/AuthDomainAssembly.swift b/Projects/Domain/AuthDomain/Sources/Assembly/AuthDomainAssembly.swift index 7c69b454..4b2e0666 100644 --- a/Projects/Domain/AuthDomain/Sources/Assembly/AuthDomainAssembly.swift +++ b/Projects/Domain/AuthDomain/Sources/Assembly/AuthDomainAssembly.swift @@ -42,9 +42,5 @@ public final class AuthDomainAssembly: Assembly { container.register(CheckIsLoggedInUseCase.self) { resolver in CheckIsLoggedInUseCaseImpl(authRepository: resolver.resolve(AuthRepository.self)!) } - - container.register(WithdrawalUseCase.self) { resolver in - WithdrawalUseCaseImpl(authRepository: resolver.resolve(AuthRepository.self)!) - } } } diff --git a/Projects/Domain/AuthDomain/Sources/DataSource/Remote/AuthEndpoint.swift b/Projects/Domain/AuthDomain/Sources/DataSource/Remote/AuthEndpoint.swift index 255deb74..5a107adc 100644 --- a/Projects/Domain/AuthDomain/Sources/DataSource/Remote/AuthEndpoint.swift +++ b/Projects/Domain/AuthDomain/Sources/DataSource/Remote/AuthEndpoint.swift @@ -1,10 +1,10 @@ +import AuthDomainInterface import Emdpoint import NetworkingInterface public enum AuthEndpoint { case signin(email: String, password: String) case refresh - case withdrawal } extension AuthEndpoint: DotoriEndpoint { @@ -19,9 +19,6 @@ extension AuthEndpoint: DotoriEndpoint { case .refresh: return .patch("") - - case .withdrawal: - return .post("/withdrawal") } } @@ -47,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 [:] + } + } } diff --git a/Projects/Domain/AuthDomain/Sources/DataSource/Remote/RemoteAuthDataSourceImpl.swift b/Projects/Domain/AuthDomain/Sources/DataSource/Remote/RemoteAuthDataSourceImpl.swift index a91ad598..80bae8ee 100644 --- a/Projects/Domain/AuthDomain/Sources/DataSource/Remote/RemoteAuthDataSourceImpl.swift +++ b/Projects/Domain/AuthDomain/Sources/DataSource/Remote/RemoteAuthDataSourceImpl.swift @@ -38,8 +38,4 @@ final class RemoteAuthDataSourceImpl: RemoteAuthDataSource { monitor.start(queue: DispatchQueue(label: "InternetConnectionMonitor")) }) } - - func withdrawal() async throws { - try await networking.request(AuthEndpoint.withdrawal) - } } diff --git a/Projects/Domain/AuthDomain/Sources/Repository/AuthRepositoryImpl.swift b/Projects/Domain/AuthDomain/Sources/Repository/AuthRepositoryImpl.swift index c56403fc..4ec220ef 100644 --- a/Projects/Domain/AuthDomain/Sources/Repository/AuthRepositoryImpl.swift +++ b/Projects/Domain/AuthDomain/Sources/Repository/AuthRepositoryImpl.swift @@ -32,8 +32,4 @@ final class AuthRepositoryImpl: AuthRepository { func networkIsConnected() async -> Bool { await remoteAuthDataSource.networkIsConnected() } - - func withdrawal() async throws { - try await remoteAuthDataSource.withdrawal() - } } diff --git a/Projects/Domain/AuthDomain/Sources/UseCase/WithdrawalUseCaseImpl.swift b/Projects/Domain/AuthDomain/Sources/UseCase/WithdrawalUseCaseImpl.swift deleted file mode 100644 index 418c2030..00000000 --- a/Projects/Domain/AuthDomain/Sources/UseCase/WithdrawalUseCaseImpl.swift +++ /dev/null @@ -1,13 +0,0 @@ -import AuthDomainInterface - -struct WithdrawalUseCaseImpl: WithdrawalUseCase { - private let authRepository: any AuthRepository - - init(authRepository: any AuthRepository) { - self.authRepository = authRepository - } - - func callAsFunction() async throws { - try await authRepository.withdrawal() - } -} diff --git a/Projects/Domain/AuthDomain/Testing/Repository/AuthRepositorySpy.swift b/Projects/Domain/AuthDomain/Testing/Repository/AuthRepositorySpy.swift index 72dd68da..e650e707 100644 --- a/Projects/Domain/AuthDomain/Testing/Repository/AuthRepositorySpy.swift +++ b/Projects/Domain/AuthDomain/Testing/Repository/AuthRepositorySpy.swift @@ -43,9 +43,4 @@ final class AuthRepositorySpy: AuthRepository { checkTokenIsExistCallCount += 1 return checkTokenIsExistReturn } - - var withdrawalCallCount = 0 - func withdrawal() async throws { - withdrawalCallCount += 1 - } } diff --git a/Projects/Domain/UserDomain/Interface/DataSource/RemoteUserDataSource.swift b/Projects/Domain/UserDomain/Interface/DataSource/RemoteUserDataSource.swift new file mode 100644 index 00000000..aab6cc98 --- /dev/null +++ b/Projects/Domain/UserDomain/Interface/DataSource/RemoteUserDataSource.swift @@ -0,0 +1,3 @@ +public protocol RemoteUserDataSource { + func withdrawal() async throws +} diff --git a/Projects/Domain/UserDomain/Interface/Repository/UserRepository.swift b/Projects/Domain/UserDomain/Interface/Repository/UserRepository.swift index 2b30f88a..f0e05e6d 100644 --- a/Projects/Domain/UserDomain/Interface/Repository/UserRepository.swift +++ b/Projects/Domain/UserDomain/Interface/Repository/UserRepository.swift @@ -4,4 +4,5 @@ import Foundation public protocol UserRepository { func loadCurrentUserRole() throws -> UserRoleType func logout() + func withdrawal() async throws } diff --git a/Projects/Domain/AuthDomain/Interface/UseCase/WithdrawalUseCase.swift b/Projects/Domain/UserDomain/Interface/UseCase/WithdrawalUseCase.swift similarity index 100% rename from Projects/Domain/AuthDomain/Interface/UseCase/WithdrawalUseCase.swift rename to Projects/Domain/UserDomain/Interface/UseCase/WithdrawalUseCase.swift diff --git a/Projects/Domain/UserDomain/Sources/Assembly/UserDomainAssembly.swift b/Projects/Domain/UserDomain/Sources/Assembly/UserDomainAssembly.swift index 38855a83..329dc0ac 100644 --- a/Projects/Domain/UserDomain/Sources/Assembly/UserDomainAssembly.swift +++ b/Projects/Domain/UserDomain/Sources/Assembly/UserDomainAssembly.swift @@ -1,5 +1,6 @@ import JwtStoreInterface import KeyValueStoreInterface +import NetworkingInterface import Swinject import UserDomainInterface @@ -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) @@ -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)!) + } } } diff --git a/Projects/Domain/UserDomain/Sources/DataSource/LocalUserDataSourceImpl.swift b/Projects/Domain/UserDomain/Sources/DataSource/Local/LocalUserDataSourceImpl.swift similarity index 100% rename from Projects/Domain/UserDomain/Sources/DataSource/LocalUserDataSourceImpl.swift rename to Projects/Domain/UserDomain/Sources/DataSource/Local/LocalUserDataSourceImpl.swift diff --git a/Projects/Domain/UserDomain/Sources/DataSource/Remote/RemoteUserDataSourceImpl.swift b/Projects/Domain/UserDomain/Sources/DataSource/Remote/RemoteUserDataSourceImpl.swift new file mode 100644 index 00000000..59f7b293 --- /dev/null +++ b/Projects/Domain/UserDomain/Sources/DataSource/Remote/RemoteUserDataSourceImpl.swift @@ -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) + } +} diff --git a/Projects/Domain/UserDomain/Sources/DataSource/Remote/UserEndpoint.swift b/Projects/Domain/UserDomain/Sources/DataSource/Remote/UserEndpoint.swift new file mode 100644 index 00000000..4bc7168b --- /dev/null +++ b/Projects/Domain/UserDomain/Sources/DataSource/Remote/UserEndpoint.swift @@ -0,0 +1,41 @@ +import AuthDomainInterface +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 [:] + } + } +} diff --git a/Projects/Domain/UserDomain/Sources/Repository/UserRepositoryImpl.swift b/Projects/Domain/UserDomain/Sources/Repository/UserRepositoryImpl.swift index 90816c0e..34d2a90e 100644 --- a/Projects/Domain/UserDomain/Sources/Repository/UserRepositoryImpl.swift +++ b/Projects/Domain/UserDomain/Sources/Repository/UserRepositoryImpl.swift @@ -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 { @@ -16,4 +21,8 @@ final class UserRepositoryImpl: UserRepository { func logout() { localUserDataSource.logout() } + + func withdrawal() async throws { + try await remoteUserDataSource.withdrawal() + } } diff --git a/Projects/Domain/UserDomain/Sources/UseCase/WithdrawalUseCaseImpl.swift b/Projects/Domain/UserDomain/Sources/UseCase/WithdrawalUseCaseImpl.swift new file mode 100644 index 00000000..f077858f --- /dev/null +++ b/Projects/Domain/UserDomain/Sources/UseCase/WithdrawalUseCaseImpl.swift @@ -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() + } +} diff --git a/Projects/Domain/UserDomain/Testing/DataSource/RemoteUserDataSourceSpy.swift b/Projects/Domain/UserDomain/Testing/DataSource/RemoteUserDataSourceSpy.swift new file mode 100644 index 00000000..b985e915 --- /dev/null +++ b/Projects/Domain/UserDomain/Testing/DataSource/RemoteUserDataSourceSpy.swift @@ -0,0 +1,9 @@ +import Foundation +import UserDomainInterface + +final class RemoteUserDataSourceSpy: RemoteUserDataSource { + var withdrawalCallCount = 0 + func withdrawal() async throws { + withdrawalCallCount += 1 + } +} diff --git a/Projects/Domain/UserDomain/Testing/Repository/UserRepositorySpy.swift b/Projects/Domain/UserDomain/Testing/Repository/UserRepositorySpy.swift index 08c7d7a5..d12c0d91 100644 --- a/Projects/Domain/UserDomain/Testing/Repository/UserRepositorySpy.swift +++ b/Projects/Domain/UserDomain/Testing/Repository/UserRepositorySpy.swift @@ -14,4 +14,9 @@ final class UserRepositorySpy: UserRepository { func logout() { logoutCallCount += 1 } + + var withdrawalCallCount = 0 + func withdrawal() async throws { + withdrawalCallCount += 1 + } } diff --git a/Projects/Domain/AuthDomain/Testing/UseCase/WithdrawalUseCaseSpy.swift b/Projects/Domain/UserDomain/Testing/UseCase/WithdrawalUseCaseSpy.swift similarity index 86% rename from Projects/Domain/AuthDomain/Testing/UseCase/WithdrawalUseCaseSpy.swift rename to Projects/Domain/UserDomain/Testing/UseCase/WithdrawalUseCaseSpy.swift index 76ce75f3..5b2739a9 100644 --- a/Projects/Domain/AuthDomain/Testing/UseCase/WithdrawalUseCaseSpy.swift +++ b/Projects/Domain/UserDomain/Testing/UseCase/WithdrawalUseCaseSpy.swift @@ -1,4 +1,4 @@ -import AuthDomainInterface +import UserDomainInterface final class WithdrawalUseCaseSpy: WithdrawalUseCase { var withdrawalCallCount = 0 diff --git a/Projects/Domain/UserDomain/Tests/UserRepositoryTest.swift b/Projects/Domain/UserDomain/Tests/UserRepositoryTest.swift index ce01b5dd..3b34bf5f 100644 --- a/Projects/Domain/UserDomain/Tests/UserRepositoryTest.swift +++ b/Projects/Domain/UserDomain/Tests/UserRepositoryTest.swift @@ -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() } diff --git a/Projects/Feature/HomeFeature/Sources/Assembly/HomeAssembly.swift b/Projects/Feature/HomeFeature/Sources/Assembly/HomeAssembly.swift index df5ed404..83dea5dd 100644 --- a/Projects/Feature/HomeFeature/Sources/Assembly/HomeAssembly.swift +++ b/Projects/Feature/HomeFeature/Sources/Assembly/HomeAssembly.swift @@ -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)! diff --git a/Projects/Feature/HomeFeature/Sources/Factory/HomeFactoryImpl.swift b/Projects/Feature/HomeFeature/Sources/Factory/HomeFactoryImpl.swift index f9a8ebc9..be7f8970 100644 --- a/Projects/Feature/HomeFeature/Sources/Factory/HomeFactoryImpl.swift +++ b/Projects/Feature/HomeFeature/Sources/Factory/HomeFactoryImpl.swift @@ -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 @@ -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 @@ -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 @@ -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( diff --git a/Projects/Feature/HomeFeature/Sources/Scene/Store/HomeStore.swift b/Projects/Feature/HomeFeature/Sources/Scene/Store/HomeStore.swift index 556dce31..21fa22db 100644 --- a/Projects/Feature/HomeFeature/Sources/Scene/Store/HomeStore.swift +++ b/Projects/Feature/HomeFeature/Sources/Scene/Store/HomeStore.swift @@ -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, @@ -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) @@ -64,6 +66,7 @@ final class HomeStore: BaseStore { self.cancelMassageUseCase = cancelMassageUseCase self.modifyMassagePersonnelUseCase = modifyMassagePersonnelUseCase self.logoutUseCase = logoutUseCase + self.withdrawalUseCase = withdrawalUseCase } enum Action: Equatable { @@ -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) diff --git a/Projects/Feature/HomeFeature/Tests/HomeFeatureTest.swift b/Projects/Feature/HomeFeature/Tests/HomeFeatureTest.swift index 5f829fa2..026bbacf 100644 --- a/Projects/Feature/HomeFeature/Tests/HomeFeatureTest.swift +++ b/Projects/Feature/HomeFeature/Tests/HomeFeatureTest.swift @@ -5,6 +5,7 @@ import XCTest @testable import HomeFeature @testable import TimerTesting @testable import SelfStudyDomainTesting +@testable import AuthDomainTesting @testable import MassageDomainTesting @testable import MealDomainTesting @testable import UserDomainTesting @@ -22,6 +23,7 @@ final class HomeFeatureTests: XCTestCase { var cancelMassageUseCase: CancelMassageUseCaseSpy! var modifyMassagePersonnelUseCase: ModifyMassagePersonnelUseCaseSpy! var logoutUseCase: LogoutUseCaseSpy! + var withdrawalUseCase: WithdrawalUseCaseSpy! var sut: HomeStore! var subscription: Set! @@ -38,6 +40,7 @@ final class HomeFeatureTests: XCTestCase { cancelMassageUseCase = .init() modifyMassagePersonnelUseCase = .init() logoutUseCase = .init() + withdrawalUseCase = .init() sut = .init( repeatableTimer: repeatableTimer, fetchSelfStudyInfoUseCase: fetchSelfStudyInfoUseCase, @@ -50,7 +53,8 @@ final class HomeFeatureTests: XCTestCase { applyMassageUseCase: applyMassageUseCase, cancelMassageUseCase: cancelMassageUseCase, modifyMassagePersonnelUseCase: modifyMassagePersonnelUseCase, - logoutUseCase: logoutUseCase + logoutUseCase: logoutUseCase, + withdrawalUseCase: withdrawalUseCase ) subscription = .init() } @@ -68,6 +72,7 @@ final class HomeFeatureTests: XCTestCase { cancelMassageUseCase = nil modifyMassagePersonnelUseCase = nil logoutUseCase = nil + withdrawalUseCase = nil sut = nil subscription = nil } diff --git a/Projects/UserInterface/Localization/Resources/en.lproj/Home.strings b/Projects/UserInterface/Localization/Resources/en.lproj/Home.strings index 95e222aa..51f7baa3 100644 --- a/Projects/UserInterface/Localization/Resources/en.lproj/Home.strings +++ b/Projects/UserInterface/Localization/Resources/en.lproj/Home.strings @@ -41,3 +41,5 @@ "complete_to_modify_self_study_limit_title" = "Complete to modify SelfStudy limit"; "massage_modify_limit_title" = "Modify Massage limit"; "complete_to_modify_massage_limit_title" = "Complete to modify Massage limit"; +"withdrawal_title" = "Withdrawal"; +"really_withdrawal_title" = "Are you sure you want to withdrawal Dotori?"; diff --git a/Projects/UserInterface/Localization/Resources/ko.lproj/Home.strings b/Projects/UserInterface/Localization/Resources/ko.lproj/Home.strings index b3e73e3b..99feac98 100644 --- a/Projects/UserInterface/Localization/Resources/ko.lproj/Home.strings +++ b/Projects/UserInterface/Localization/Resources/ko.lproj/Home.strings @@ -41,3 +41,5 @@ "complete_to_modify_self_study_limit_title" = "자습 인원 수정 완료"; "massage_modify_limit_title" = "안마의자 인원 수정"; "complete_to_modify_massage_limit_title" = "안마의자 인원 수정 완료"; +"withdrawal_title" = "회원탈퇴"; +"really_withdrawal_title" = "정말로 도토리를 회원탈퇴 하시겠습니까?"; From ad940864eeacc2b4d4dbe221741957aa5b544f0a Mon Sep 17 00:00:00 2001 From: baegteun Date: Wed, 2 Aug 2023 22:09:44 +0900 Subject: [PATCH 4/6] =?UTF-8?q?:pencil2:=20::=20[#144]=20UserDomain=20/=20?= =?UTF-8?q?UserEndpoint=EC=97=90=20=EC=8B=A4=EC=88=98=ED=95=9C=20import=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/DataSource/Remote/UserEndpoint.swift | 1 - .../Sources/Scene/HomeViewController.swift | 12 +++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/Projects/Domain/UserDomain/Sources/DataSource/Remote/UserEndpoint.swift b/Projects/Domain/UserDomain/Sources/DataSource/Remote/UserEndpoint.swift index 4bc7168b..89ea5a94 100644 --- a/Projects/Domain/UserDomain/Sources/DataSource/Remote/UserEndpoint.swift +++ b/Projects/Domain/UserDomain/Sources/DataSource/Remote/UserEndpoint.swift @@ -1,4 +1,3 @@ -import AuthDomainInterface import Emdpoint import NetworkingInterface diff --git a/Projects/Feature/HomeFeature/Sources/Scene/HomeViewController.swift b/Projects/Feature/HomeFeature/Sources/Scene/HomeViewController.swift index ce740d7e..c7060c35 100644 --- a/Projects/Feature/HomeFeature/Sources/Scene/HomeViewController.swift +++ b/Projects/Feature/HomeFeature/Sources/Scene/HomeViewController.swift @@ -27,6 +27,7 @@ final class HomeViewController: BaseStoredViewController { maxApplyCount: 5 ) private let mealCardView = MealCardView() + private let bottomSpacerView = SpacerView(height: 40) override func setLayout() { MSGLayout.stackedScrollLayout(view) { @@ -41,7 +42,7 @@ final class HomeViewController: BaseStoredViewController { mealCardView - SpacerView(height: 32) + bottomSpacerView } .margin(.horizontal(20)) } @@ -108,6 +109,15 @@ final class HomeViewController: BaseStoredViewController { .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() { From ea21cb4e6b028f4dd25aca176d85836e668ac3ba Mon Sep 17 00:00:00 2001 From: baegteun Date: Wed, 2 Aug 2023 22:22:56 +0900 Subject: [PATCH 5/6] =?UTF-8?q?:pencil2:=20::=20[#144]=20HomeFeature=20/?= =?UTF-8?q?=20Test=EC=97=90=20=EC=A3=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Feature/HomeFeature/Sources/Scene/HomeViewController.swift | 1 + Projects/Feature/HomeFeature/Tests/HomeFeatureTest.swift | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/Projects/Feature/HomeFeature/Sources/Scene/HomeViewController.swift b/Projects/Feature/HomeFeature/Sources/Scene/HomeViewController.swift index c7060c35..6ad67ab1 100644 --- a/Projects/Feature/HomeFeature/Sources/Scene/HomeViewController.swift +++ b/Projects/Feature/HomeFeature/Sources/Scene/HomeViewController.swift @@ -28,6 +28,7 @@ final class HomeViewController: BaseStoredViewController { ) private let mealCardView = MealCardView() private let bottomSpacerView = SpacerView(height: 40) + .set(\.backgroundColor, .clear.withAlphaComponent(0.0125)) override func setLayout() { MSGLayout.stackedScrollLayout(view) { diff --git a/Projects/Feature/HomeFeature/Tests/HomeFeatureTest.swift b/Projects/Feature/HomeFeature/Tests/HomeFeatureTest.swift index 026bbacf..b5519c5b 100644 --- a/Projects/Feature/HomeFeature/Tests/HomeFeatureTest.swift +++ b/Projects/Feature/HomeFeature/Tests/HomeFeatureTest.swift @@ -5,7 +5,6 @@ import XCTest @testable import HomeFeature @testable import TimerTesting @testable import SelfStudyDomainTesting -@testable import AuthDomainTesting @testable import MassageDomainTesting @testable import MealDomainTesting @testable import UserDomainTesting From d8a9af0d402336be8aeafa28f1e8fdb2a928dd2e Mon Sep 17 00:00:00 2001 From: baegteun Date: Wed, 2 Aug 2023 22:30:55 +0900 Subject: [PATCH 6/6] =?UTF-8?q?:pencil2:=20::=20[#144]=20HomeFeature=20/?= =?UTF-8?q?=20Demo=EC=97=90=20UseCase=20=EC=A3=BC=EC=9E=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Projects/Feature/HomeFeature/Demo/Sources/AppDelegate.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Projects/Feature/HomeFeature/Demo/Sources/AppDelegate.swift b/Projects/Feature/HomeFeature/Demo/Sources/AppDelegate.swift index a669d5a3..3d062bf3 100644 --- a/Projects/Feature/HomeFeature/Demo/Sources/AppDelegate.swift +++ b/Projects/Feature/HomeFeature/Demo/Sources/AppDelegate.swift @@ -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, @@ -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))