Skip to content
Snippets Groups Projects
Commit db42327e authored by Dariusz Rybicki's avatar Dariusz Rybicki
Browse files

Handle auth requests in HomeFeature

parent 24beaf32
No related branches found
No related tags found
2 merge requests!102Release 1.0.0,!76Messenger example - auth requests handling
...@@ -55,6 +55,7 @@ extension AppEnvironment { ...@@ -55,6 +55,7 @@ extension AppEnvironment {
dbManager: dbManager, dbManager: dbManager,
mainQueue: mainQueue, mainQueue: mainQueue,
bgQueue: bgQueue, bgQueue: bgQueue,
now: Date.init,
register: { register: {
RegisterEnvironment( RegisterEnvironment(
messenger: messenger, messenger: messenger,
......
...@@ -6,8 +6,10 @@ import ContactsFeature ...@@ -6,8 +6,10 @@ import ContactsFeature
import Foundation import Foundation
import RegisterFeature import RegisterFeature
import UserSearchFeature import UserSearchFeature
import XCTestDynamicOverlay
import XXClient import XXClient
import XXMessengerClient import XXMessengerClient
import XXModels
public struct HomeState: Equatable { public struct HomeState: Equatable {
public init( public init(
...@@ -47,6 +49,12 @@ public enum HomeAction: Equatable { ...@@ -47,6 +49,12 @@ public enum HomeAction: Equatable {
case failure(NSError) case failure(NSError)
} }
public enum AuthCallbacks: Equatable {
case register
case unregister
case handle(XXClient.AuthCallbacks.Callback)
}
public enum NetworkMonitor: Equatable { public enum NetworkMonitor: Equatable {
case start case start
case stop case stop
...@@ -62,6 +70,7 @@ public enum HomeAction: Equatable { ...@@ -62,6 +70,7 @@ public enum HomeAction: Equatable {
} }
case messenger(Messenger) case messenger(Messenger)
case authCallbacks(AuthCallbacks)
case networkMonitor(NetworkMonitor) case networkMonitor(NetworkMonitor)
case deleteAccount(DeleteAccount) case deleteAccount(DeleteAccount)
case didDismissAlert case didDismissAlert
...@@ -81,6 +90,7 @@ public struct HomeEnvironment { ...@@ -81,6 +90,7 @@ public struct HomeEnvironment {
dbManager: DBManager, dbManager: DBManager,
mainQueue: AnySchedulerOf<DispatchQueue>, mainQueue: AnySchedulerOf<DispatchQueue>,
bgQueue: AnySchedulerOf<DispatchQueue>, bgQueue: AnySchedulerOf<DispatchQueue>,
now: @escaping () -> Date,
register: @escaping () -> RegisterEnvironment, register: @escaping () -> RegisterEnvironment,
contacts: @escaping () -> ContactsEnvironment, contacts: @escaping () -> ContactsEnvironment,
userSearch: @escaping () -> UserSearchEnvironment userSearch: @escaping () -> UserSearchEnvironment
...@@ -89,6 +99,7 @@ public struct HomeEnvironment { ...@@ -89,6 +99,7 @@ public struct HomeEnvironment {
self.dbManager = dbManager self.dbManager = dbManager
self.mainQueue = mainQueue self.mainQueue = mainQueue
self.bgQueue = bgQueue self.bgQueue = bgQueue
self.now = now
self.register = register self.register = register
self.contacts = contacts self.contacts = contacts
self.userSearch = userSearch self.userSearch = userSearch
...@@ -98,6 +109,7 @@ public struct HomeEnvironment { ...@@ -98,6 +109,7 @@ public struct HomeEnvironment {
public var dbManager: DBManager public var dbManager: DBManager
public var mainQueue: AnySchedulerOf<DispatchQueue> public var mainQueue: AnySchedulerOf<DispatchQueue>
public var bgQueue: AnySchedulerOf<DispatchQueue> public var bgQueue: AnySchedulerOf<DispatchQueue>
public var now: () -> Date
public var register: () -> RegisterEnvironment public var register: () -> RegisterEnvironment
public var contacts: () -> ContactsEnvironment public var contacts: () -> ContactsEnvironment
public var userSearch: () -> UserSearchEnvironment public var userSearch: () -> UserSearchEnvironment
...@@ -109,6 +121,7 @@ extension HomeEnvironment { ...@@ -109,6 +121,7 @@ extension HomeEnvironment {
dbManager: .unimplemented, dbManager: .unimplemented,
mainQueue: .unimplemented, mainQueue: .unimplemented,
bgQueue: .unimplemented, bgQueue: .unimplemented,
now: XCTUnimplemented("\(Self.self).now", placeholder: Date()),
register: { .unimplemented }, register: { .unimplemented },
contacts: { .unimplemented }, contacts: { .unimplemented },
userSearch: { .unimplemented } userSearch: { .unimplemented }
...@@ -119,10 +132,12 @@ public let homeReducer = Reducer<HomeState, HomeAction, HomeEnvironment> ...@@ -119,10 +132,12 @@ public let homeReducer = Reducer<HomeState, HomeAction, HomeEnvironment>
{ state, action, env in { state, action, env in
enum NetworkHealthEffectId {} enum NetworkHealthEffectId {}
enum NetworkNodesEffectId {} enum NetworkNodesEffectId {}
enum AuthCallbacksEffectId {}
switch action { switch action {
case .messenger(.start): case .messenger(.start):
return .merge( return .merge(
Effect(value: .authCallbacks(.register)),
Effect(value: .networkMonitor(.stop)), Effect(value: .networkMonitor(.stop)),
Effect.result { Effect.result {
do { do {
...@@ -160,6 +175,59 @@ public let homeReducer = Reducer<HomeState, HomeAction, HomeEnvironment> ...@@ -160,6 +175,59 @@ public let homeReducer = Reducer<HomeState, HomeAction, HomeEnvironment>
state.failure = error.localizedDescription state.failure = error.localizedDescription
return .none return .none
case .authCallbacks(.register):
return Effect.run { subscriber in
let handler = AuthCallbacks { callback in
subscriber.send(.authCallbacks(.handle(callback)))
}
let cancellable = env.messenger.registerAuthCallbacks(handler)
return AnyCancellable { cancellable.cancel() }
}
.subscribe(on: env.bgQueue)
.receive(on: env.mainQueue)
.eraseToEffect()
.cancellable(id: AuthCallbacksEffectId.self, cancelInFlight: true)
case .authCallbacks(.unregister):
return .cancel(id: AuthCallbacksEffectId.self)
case .authCallbacks(.handle(.request(let contact, _, _, _))):
return .fireAndForget {
let db = try env.dbManager.getDB()
let contactId = try contact.getId()
guard try db.fetchContacts(.init(id: [contactId])).isEmpty else {
return
}
var dbContact = XXModels.Contact(id: contactId)
dbContact.marshaled = contact.data
dbContact.username = try contact.getFact(.username)?.value
dbContact.email = try contact.getFact(.email)?.value
dbContact.phone = try contact.getFact(.phone)?.value
dbContact.authStatus = .verificationInProgress
dbContact.createdAt = env.now()
dbContact = try db.saveContact(dbContact)
}
.subscribe(on: env.bgQueue)
.receive(on: env.mainQueue)
.eraseToEffect()
case .authCallbacks(.handle(.confirm(let contact, _, _, _))):
return .fireAndForget {
let db = try env.dbManager.getDB()
let contactId = try contact.getId()
guard var dbContact = try db.fetchContacts(.init(id: [contactId])).first else {
return
}
dbContact.authStatus = .friend
dbContact = try db.saveContact(dbContact)
}
.subscribe(on: env.bgQueue)
.receive(on: env.mainQueue)
.eraseToEffect()
case .authCallbacks(.handle(.reset(let contact, _, _, _))):
return .none
case .networkMonitor(.start): case .networkMonitor(.start):
return .merge( return .merge(
Effect.run { subscriber in Effect.run { subscriber in
......
...@@ -21,6 +21,7 @@ final class HomeFeatureTests: XCTestCase { ...@@ -21,6 +21,7 @@ final class HomeFeatureTests: XCTestCase {
store.environment.bgQueue = .immediate store.environment.bgQueue = .immediate
store.environment.mainQueue = .immediate store.environment.mainQueue = .immediate
store.environment.messenger.registerAuthCallbacks.run = { _ in Cancellable {} }
store.environment.messenger.start.run = { messengerDidStartWithTimeout.append($0) } store.environment.messenger.start.run = { messengerDidStartWithTimeout.append($0) }
store.environment.messenger.isConnected.run = { false } store.environment.messenger.isConnected.run = { false }
store.environment.messenger.connect.run = { messengerDidConnect += 1 } store.environment.messenger.connect.run = { messengerDidConnect += 1 }
...@@ -32,10 +33,13 @@ final class HomeFeatureTests: XCTestCase { ...@@ -32,10 +33,13 @@ final class HomeFeatureTests: XCTestCase {
XCTAssertNoDifference(messengerDidStartWithTimeout, [30_000]) XCTAssertNoDifference(messengerDidStartWithTimeout, [30_000])
XCTAssertNoDifference(messengerDidConnect, 1) XCTAssertNoDifference(messengerDidConnect, 1)
store.receive(.authCallbacks(.register))
store.receive(.networkMonitor(.stop)) store.receive(.networkMonitor(.stop))
store.receive(.messenger(.didStartUnregistered)) { store.receive(.messenger(.didStartUnregistered)) {
$0.register = RegisterState() $0.register = RegisterState()
} }
store.send(.authCallbacks(.unregister))
} }
func testMessengerStartRegistered() { func testMessengerStartRegistered() {
...@@ -51,6 +55,7 @@ final class HomeFeatureTests: XCTestCase { ...@@ -51,6 +55,7 @@ final class HomeFeatureTests: XCTestCase {
store.environment.bgQueue = .immediate store.environment.bgQueue = .immediate
store.environment.mainQueue = .immediate store.environment.mainQueue = .immediate
store.environment.messenger.registerAuthCallbacks.run = { _ in Cancellable {} }
store.environment.messenger.start.run = { messengerDidStartWithTimeout.append($0) } store.environment.messenger.start.run = { messengerDidStartWithTimeout.append($0) }
store.environment.messenger.isConnected.run = { false } store.environment.messenger.isConnected.run = { false }
store.environment.messenger.connect.run = { messengerDidConnect += 1 } store.environment.messenger.connect.run = { messengerDidConnect += 1 }
...@@ -73,11 +78,14 @@ final class HomeFeatureTests: XCTestCase { ...@@ -73,11 +78,14 @@ final class HomeFeatureTests: XCTestCase {
XCTAssertNoDifference(messengerDidConnect, 1) XCTAssertNoDifference(messengerDidConnect, 1)
XCTAssertNoDifference(messengerDidLogIn, 1) XCTAssertNoDifference(messengerDidLogIn, 1)
store.receive(.authCallbacks(.register))
store.receive(.networkMonitor(.stop)) store.receive(.networkMonitor(.stop))
store.receive(.messenger(.didStartRegistered)) store.receive(.messenger(.didStartRegistered))
store.receive(.networkMonitor(.start)) store.receive(.networkMonitor(.start))
store.send(.networkMonitor(.stop)) store.send(.networkMonitor(.stop))
store.send(.authCallbacks(.unregister))
} }
func testRegisterFinished() { func testRegisterFinished() {
...@@ -94,6 +102,7 @@ final class HomeFeatureTests: XCTestCase { ...@@ -94,6 +102,7 @@ final class HomeFeatureTests: XCTestCase {
store.environment.bgQueue = .immediate store.environment.bgQueue = .immediate
store.environment.mainQueue = .immediate store.environment.mainQueue = .immediate
store.environment.messenger.registerAuthCallbacks.run = { _ in Cancellable {} }
store.environment.messenger.start.run = { messengerDidStartWithTimeout.append($0) } store.environment.messenger.start.run = { messengerDidStartWithTimeout.append($0) }
store.environment.messenger.isConnected.run = { true } store.environment.messenger.isConnected.run = { true }
store.environment.messenger.isLoggedIn.run = { false } store.environment.messenger.isLoggedIn.run = { false }
...@@ -118,11 +127,14 @@ final class HomeFeatureTests: XCTestCase { ...@@ -118,11 +127,14 @@ final class HomeFeatureTests: XCTestCase {
XCTAssertNoDifference(messengerDidStartWithTimeout, [30_000]) XCTAssertNoDifference(messengerDidStartWithTimeout, [30_000])
XCTAssertNoDifference(messengerDidLogIn, 1) XCTAssertNoDifference(messengerDidLogIn, 1)
store.receive(.authCallbacks(.register))
store.receive(.networkMonitor(.stop)) store.receive(.networkMonitor(.stop))
store.receive(.messenger(.didStartRegistered)) store.receive(.messenger(.didStartRegistered))
store.receive(.networkMonitor(.start)) store.receive(.networkMonitor(.start))
store.send(.networkMonitor(.stop)) store.send(.networkMonitor(.stop))
store.send(.authCallbacks(.unregister))
} }
func testMessengerStartFailure() { func testMessengerStartFailure() {
...@@ -137,14 +149,18 @@ final class HomeFeatureTests: XCTestCase { ...@@ -137,14 +149,18 @@ final class HomeFeatureTests: XCTestCase {
store.environment.bgQueue = .immediate store.environment.bgQueue = .immediate
store.environment.mainQueue = .immediate store.environment.mainQueue = .immediate
store.environment.messenger.registerAuthCallbacks.run = { _ in Cancellable {} }
store.environment.messenger.start.run = { _ in throw error } store.environment.messenger.start.run = { _ in throw error }
store.send(.messenger(.start)) store.send(.messenger(.start))
store.receive(.authCallbacks(.register))
store.receive(.networkMonitor(.stop)) store.receive(.networkMonitor(.stop))
store.receive(.messenger(.failure(error as NSError))) { store.receive(.messenger(.failure(error as NSError))) {
$0.failure = error.localizedDescription $0.failure = error.localizedDescription
} }
store.send(.authCallbacks(.unregister))
} }
func testMessengerStartConnectFailure() { func testMessengerStartConnectFailure() {
...@@ -159,16 +175,20 @@ final class HomeFeatureTests: XCTestCase { ...@@ -159,16 +175,20 @@ final class HomeFeatureTests: XCTestCase {
store.environment.bgQueue = .immediate store.environment.bgQueue = .immediate
store.environment.mainQueue = .immediate store.environment.mainQueue = .immediate
store.environment.messenger.registerAuthCallbacks.run = { _ in Cancellable {} }
store.environment.messenger.start.run = { _ in } store.environment.messenger.start.run = { _ in }
store.environment.messenger.isConnected.run = { false } store.environment.messenger.isConnected.run = { false }
store.environment.messenger.connect.run = { throw error } store.environment.messenger.connect.run = { throw error }
store.send(.messenger(.start)) store.send(.messenger(.start))
store.receive(.authCallbacks(.register))
store.receive(.networkMonitor(.stop)) store.receive(.networkMonitor(.stop))
store.receive(.messenger(.failure(error as NSError))) { store.receive(.messenger(.failure(error as NSError))) {
$0.failure = error.localizedDescription $0.failure = error.localizedDescription
} }
store.send(.authCallbacks(.unregister))
} }
func testMessengerStartIsRegisteredFailure() { func testMessengerStartIsRegisteredFailure() {
...@@ -183,6 +203,7 @@ final class HomeFeatureTests: XCTestCase { ...@@ -183,6 +203,7 @@ final class HomeFeatureTests: XCTestCase {
store.environment.bgQueue = .immediate store.environment.bgQueue = .immediate
store.environment.mainQueue = .immediate store.environment.mainQueue = .immediate
store.environment.messenger.registerAuthCallbacks.run = { _ in Cancellable {} }
store.environment.messenger.start.run = { _ in } store.environment.messenger.start.run = { _ in }
store.environment.messenger.isConnected.run = { true } store.environment.messenger.isConnected.run = { true }
store.environment.messenger.isLoggedIn.run = { false } store.environment.messenger.isLoggedIn.run = { false }
...@@ -190,10 +211,13 @@ final class HomeFeatureTests: XCTestCase { ...@@ -190,10 +211,13 @@ final class HomeFeatureTests: XCTestCase {
store.send(.messenger(.start)) store.send(.messenger(.start))
store.receive(.authCallbacks(.register))
store.receive(.networkMonitor(.stop)) store.receive(.networkMonitor(.stop))
store.receive(.messenger(.failure(error as NSError))) { store.receive(.messenger(.failure(error as NSError))) {
$0.failure = error.localizedDescription $0.failure = error.localizedDescription
} }
store.send(.authCallbacks(.unregister))
} }
func testMessengerStartLogInFailure() { func testMessengerStartLogInFailure() {
...@@ -208,6 +232,7 @@ final class HomeFeatureTests: XCTestCase { ...@@ -208,6 +232,7 @@ final class HomeFeatureTests: XCTestCase {
store.environment.bgQueue = .immediate store.environment.bgQueue = .immediate
store.environment.mainQueue = .immediate store.environment.mainQueue = .immediate
store.environment.messenger.registerAuthCallbacks.run = { _ in Cancellable {} }
store.environment.messenger.start.run = { _ in } store.environment.messenger.start.run = { _ in }
store.environment.messenger.isConnected.run = { true } store.environment.messenger.isConnected.run = { true }
store.environment.messenger.isLoggedIn.run = { false } store.environment.messenger.isLoggedIn.run = { false }
...@@ -216,10 +241,13 @@ final class HomeFeatureTests: XCTestCase { ...@@ -216,10 +241,13 @@ final class HomeFeatureTests: XCTestCase {
store.send(.messenger(.start)) store.send(.messenger(.start))
store.receive(.authCallbacks(.register))
store.receive(.networkMonitor(.stop)) store.receive(.networkMonitor(.stop))
store.receive(.messenger(.failure(error as NSError))) { store.receive(.messenger(.failure(error as NSError))) {
$0.failure = error.localizedDescription $0.failure = error.localizedDescription
} }
store.send(.authCallbacks(.unregister))
} }
func testNetworkMonitorStart() { func testNetworkMonitorStart() {
...@@ -491,4 +519,109 @@ final class HomeFeatureTests: XCTestCase { ...@@ -491,4 +519,109 @@ final class HomeFeatureTests: XCTestCase {
$0.contacts = nil $0.contacts = nil
} }
} }
func testAuthCallbacks() {
let store = TestStore(
initialState: HomeState(),
reducer: homeReducer,
environment: .unimplemented
)
let now = Date()
var didRegisterCallbacks: [AuthCallbacks] = []
var didCancelAuthCallbacks = 0
var dbContact: XXModels.Contact?
var dbDidSaveContact: [XXModels.Contact] = []
store.environment.mainQueue = .immediate
store.environment.bgQueue = .immediate
store.environment.now = { now }
store.environment.messenger.registerAuthCallbacks.run = { callbacks in
didRegisterCallbacks.append(callbacks)
return Cancellable { didCancelAuthCallbacks += 1 }
}
store.environment.dbManager.getDB.run = {
var db: Database = .failing
db.fetchContacts.run = { _ in [dbContact].compactMap { $0 } }
db.saveContact.run = { contact in
dbDidSaveContact.append(contact)
return contact
}
return db
}
store.send(.authCallbacks(.register))
XCTAssertNoDifference(didRegisterCallbacks.count, 1)
var contact = XXClient.Contact.unimplemented("data".data(using: .utf8)!)
contact.getIdFromContact.run = { _ in "id".data(using: .utf8)! }
contact.getFactsFromContact.run = { _ in
[
Fact(type: .username, value: "username"),
Fact(type: .email, value: "email"),
Fact(type: .phone, value: "phone"),
]
}
dbContact = nil
dbDidSaveContact = []
didRegisterCallbacks.first?.handle(
.request(contact: contact, receptionId: Data(), ephemeralId: 0, roundId: 0)
)
store.receive(.authCallbacks(.handle(
.request(contact: contact, receptionId: Data(), ephemeralId: 0, roundId: 0)
)))
XCTAssertNoDifference(dbDidSaveContact, [.init(
id: "id".data(using: .utf8)!,
marshaled: "data".data(using: .utf8)!,
username: "username",
email: "email",
phone: "phone",
authStatus: .verificationInProgress,
createdAt: now
)])
dbContact = .init(id: "id".data(using: .utf8)!)
dbDidSaveContact = []
didRegisterCallbacks.first?.handle(
.request(contact: contact, receptionId: Data(), ephemeralId: 0, roundId: 0)
)
store.receive(.authCallbacks(.handle(
.request(contact: contact, receptionId: Data(), ephemeralId: 0, roundId: 0)
)))
XCTAssertNoDifference(dbDidSaveContact, [])
dbContact = .init(id: "id".data(using: .utf8)!)
dbDidSaveContact = []
didRegisterCallbacks.first?.handle(
.confirm(contact: contact, receptionId: Data(), ephemeralId: 0, roundId: 0)
)
store.receive(.authCallbacks(.handle(
.confirm(contact: contact, receptionId: Data(), ephemeralId: 0, roundId: 0)
)))
XCTAssertNoDifference(dbDidSaveContact, [.init(
id: "id".data(using: .utf8)!,
authStatus: .friend,
createdAt: dbContact!.createdAt
)])
dbContact = nil
dbDidSaveContact = []
didRegisterCallbacks.first?.handle(
.reset(contact: contact, receptionId: Data(), ephemeralId: 0, roundId: 0)
)
store.receive(.authCallbacks(.handle(
.reset(contact: contact, receptionId: Data(), ephemeralId: 0, roundId: 0)
)))
XCTAssertNoDifference(dbDidSaveContact, [])
store.send(.authCallbacks(.unregister))
XCTAssertNoDifference(didCancelAuthCallbacks, 1)
}
} }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment