From 24c8aa7cdf8500e5e6637eea4d82fdf6d456f60b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franc=CC=A7ois=20Benaiteau?= Date: Mon, 13 Jan 2025 10:50:04 +0100 Subject: [PATCH 01/11] fix doc --- .../APIs/FeatureConfigsAPI/FeatureConfigsAPIBuilder.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WireAPI/Sources/WireAPI/APIs/FeatureConfigsAPI/FeatureConfigsAPIBuilder.swift b/WireAPI/Sources/WireAPI/APIs/FeatureConfigsAPI/FeatureConfigsAPIBuilder.swift index 4537ffe25b..2d75ea12c2 100644 --- a/WireAPI/Sources/WireAPI/APIs/FeatureConfigsAPI/FeatureConfigsAPIBuilder.swift +++ b/WireAPI/Sources/WireAPI/APIs/FeatureConfigsAPI/FeatureConfigsAPIBuilder.swift @@ -26,7 +26,7 @@ public struct FeatureConfigsAPIBuilder { /// Create a new builder. /// - /// - Parameter httpClient: A http client. + /// - Parameter APIService: An api service. public init(apiService: any APIServiceProtocol) { self.apiService = apiService From 6ca1664d44265a9209002e03502cf7634a62ddb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franc=CC=A7ois=20Benaiteau?= Date: Mon, 13 Jan 2025 15:20:39 +0100 Subject: [PATCH 02/11] wip --- .../UserClients/UserClientsRepository.swift | 59 ++++++++++++++ .../UseCases/PullSelfUserClients.swift | 78 +++++++++++++++++++ .../PushSupportedProtocolsUseCase.swift | 41 +++++++--- .../Synchronization/StrategyDirectory.swift | 13 +++- .../ResolveOneOnOneConversationsUseCase.swift | 31 ++++++-- .../ZMUserSession/ZMUserSession.swift | 38 +++++++-- 6 files changed, 234 insertions(+), 26 deletions(-) create mode 100644 WireDomain/Sources/WireDomain/UseCases/PullSelfUserClients.swift diff --git a/WireDomain/Sources/WireDomain/Repositories/UserClients/UserClientsRepository.swift b/WireDomain/Sources/WireDomain/Repositories/UserClients/UserClientsRepository.swift index 06f86f7ba0..1020f61751 100644 --- a/WireDomain/Sources/WireDomain/Repositories/UserClients/UserClientsRepository.swift +++ b/WireDomain/Sources/WireDomain/Repositories/UserClients/UserClientsRepository.swift @@ -113,3 +113,62 @@ public struct UserClientsRepository: UserClientsRepositoryProtocol { } } + + + + + + + +//public extension UserClientsRepository { +// +// static func make(apiService: any APIServiceProtocol, +// apiVersion: WireAPI.APIVersion, +// context: NSManagedObjectContext) -> UserClientsRepositoryProtocol { +// +// let userClientsAPI = UserClientsAPIBuilder(apiService: apiService).makeAPI(for: apiVersion) +// let usersAPI = UsersAPIBuilder(apiService: apiService).makeAPI(for: apiVersion) +// let selfUserAPI = SelfUserAPIBuilder(apiService: apiService).makeAPI(for: apiVersion) +// let userPropertiesAPI = UserPropertiesBuilder(apiService: apiService).makeAPI(for: apiVersion) +// +// +// let conversationLabelsLocalStore = ConversationLabelsLocalStore(context: context) +// +// let conversationRepository = ConversationRepository(conversationsAPI: <#T##any ConversationsAPI#>, +// conversationsLocalStore: any ConversationLocalStoreProtocol, +// userRepository: <#T##any UserRepositoryProtocol#>, +// teamRepository: <#T##any TeamRepositoryProtocol#>, +// messageRepository: <#T##any MessageRepositoryProtocol#>, +// backendInfo: <#T##ConversationRepository.BackendInfo#>, +// mlsProvider: <#T##MLSProvider#>) +// +// let conversationLabelsRepository = ConversationLabelsRepository(userPropertiesAPI: userPropertiesAPI, conversationLabelsLocalStore: conversationLabelsLocalStore) +// +// +// let userRepository = UserRepository(usersAPI: usersAPI, +// selfUserAPI: selfUserAPI, +// conversationLabelsRepository: conversationLabelsRepository, conversationRepository: <#T##any ConversationRepositoryProtocol#>, userLocalStore: <#T##any UserLocalStoreProtocol#>) +// let userClientsLocalStore = UserClientsLocalStore(context: context, userLocalStore: ) +// +// return UserClientsRepository(userClientsAPI: userClientsAPI, +// userRepository: userRepository, +// userClientsLocalStore: userClientsLocalStore) +// } +//} + +//public extension SupportedProtocolsHelper { +// +// public static func make(apiService: any APIServiceProtocol, apiVersion: WireAPI.APIVersion, context: NSManagedObjectContext) -> SupportedProtocolsHelper { +// +// let userClientsRepository = UserClientsRepository.make(apiService: apiService, apiVersion: apiVersion, context: context) +// +// let featureConfigRepository = FeatureConfigRepository( +// featureConfigsAPI: FeatureConfigsAPIBuilder(apiService: apiService).makeAPI(for: apiVersion), +// featureConfigLocalStore: FeatureConfigLocalStore(context: context) +// ) +// +// +// return SupportedProtocolsHelper(featureConfigRepository: featureConfigRepository, +// userClientsRepository: <#T##any UserClientsRepositoryProtocol#>) +// } +//} diff --git a/WireDomain/Sources/WireDomain/UseCases/PullSelfUserClients.swift b/WireDomain/Sources/WireDomain/UseCases/PullSelfUserClients.swift new file mode 100644 index 0000000000..32a39be7d1 --- /dev/null +++ b/WireDomain/Sources/WireDomain/UseCases/PullSelfUserClients.swift @@ -0,0 +1,78 @@ +// +// Wire +// Copyright (C) 2025 Wire Swiss GmbH +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see http://www.gnu.org/licenses/. +// + +import WireAPI +import CoreData + +public struct PullSelfUserClients { + private let userClientsAPI: any UserClientsAPI + private let userClientsLocalStore: any UserClientsLocalStoreProtocol + + public func pullSelfClients() async throws { + let remoteSelfClients = try await userClientsAPI.getSelfClients() + + for remoteSelfClient in remoteSelfClients { + let localUserClient = await userClientsLocalStore.fetchOrCreateClient( + id: remoteSelfClient.id + ) + + try await updateClient( + id: remoteSelfClient.id, + from: remoteSelfClient, + isNewClient: localUserClient.isNew + ) + } + + let deletedSelfClientsIDs = await userClientsLocalStore.deletedSelfClients( + newClients: remoteSelfClients.map(\.id) + ) + + for deletedSelfClientID in deletedSelfClientsIDs { + await userClientsLocalStore.deleteClient(id: deletedSelfClientID) + } + } + + + func updateClient( + id: String, + from remoteClient: WireAPI.SelfUserClient, + isNewClient: Bool + ) async throws { + await userClientsLocalStore.updateClient( + id: id, + isNewClient: isNewClient, + userClientInfo: remoteClient.toDomainModel() + ) + } + + +} + +public extension PullSelfUserClients { + + static func make(apiService: any APIServiceProtocol, + apiVersion: WireAPI.APIVersion, + context: NSManagedObjectContext) -> PullSelfUserClients { + let userClientsAPI = UserClientsAPIBuilder(apiService: apiService).makeAPI(for: apiVersion) + + let userLocalStore = UserLocalStore(context: context) + let userClientsLocalStore = UserClientsLocalStore(context: context, userLocalStore: userLocalStore) + + return PullSelfUserClients(userClientsAPI: userClientsAPI, userClientsLocalStore: userClientsLocalStore) + } +} diff --git a/WireDomain/Sources/WireDomain/UseCases/PushSupportedProtocolsUseCase.swift b/WireDomain/Sources/WireDomain/UseCases/PushSupportedProtocolsUseCase.swift index 47a7baa2fb..aa31916a8b 100644 --- a/WireDomain/Sources/WireDomain/UseCases/PushSupportedProtocolsUseCase.swift +++ b/WireDomain/Sources/WireDomain/UseCases/PushSupportedProtocolsUseCase.swift @@ -27,8 +27,7 @@ public protocol PushSupportedProtocolsUseCaseProtocol { func invoke() async throws } -public struct PushSupportedProtocolsUseCase: PushSupportedProtocolsUseCaseProtocol { - +public struct SupportedProtocolsHelper { private enum ProteusToMLSMigrationState: String { case disabled case notStarted @@ -37,17 +36,11 @@ public struct PushSupportedProtocolsUseCase: PushSupportedProtocolsUseCaseProtoc } let featureConfigRepository: any FeatureConfigRepositoryProtocol - let userRepository: any UserRepositoryProtocol let userClientsRepository: any UserClientsRepositoryProtocol private let logger = WireLogger(tag: "supported-protocols") - public func invoke() async throws { - let supportedProtocols = await calculateSupportedProtocols() - try await userRepository.pushSelfSupportedProtocols(supportedProtocols) - } - - private func calculateSupportedProtocols() async -> Set { + public func calculateSupportedProtocols() async -> Set { logger.debug("calculating supported protocols...") let remoteProtocols = await remotelySupportedProtocols() @@ -161,5 +154,35 @@ public struct PushSupportedProtocolsUseCase: PushSupportedProtocolsUseCaseProtoc private func allSelfUserClientsAreActiveMLSClients() async -> Bool { await userClientsRepository.allSelfUserClientsAreActiveMLSClients() } +} + +public struct PushSupportedProtocolsUseCase: PushSupportedProtocolsUseCaseProtocol { + + private enum ProteusToMLSMigrationState: String { + case disabled + case notStarted + case ongoing + case finalised + } + + let helper: SupportedProtocolsHelper + let userRepository: any UserRepositoryProtocol + + private let logger = WireLogger(tag: "supported-protocols") + + init(featureConfigRepository: any FeatureConfigRepositoryProtocol, + userRepository: any UserRepositoryProtocol, + userClientsRepository: any UserClientsRepositoryProtocol + ) { + + self.helper = SupportedProtocolsHelper(featureConfigRepository: featureConfigRepository, + userClientsRepository: userClientsRepository) + self.userRepository = userRepository + } + + public func invoke() async throws { + let supportedProtocols = await helper.calculateSupportedProtocols() + try await userRepository.pushSelfSupportedProtocols(supportedProtocols) + } } diff --git a/wire-ios-sync-engine/Source/Synchronization/StrategyDirectory.swift b/wire-ios-sync-engine/Source/Synchronization/StrategyDirectory.swift index 548f4ece6e..cdb390124a 100644 --- a/wire-ios-sync-engine/Source/Synchronization/StrategyDirectory.swift +++ b/wire-ios-sync-engine/Source/Synchronization/StrategyDirectory.swift @@ -54,6 +54,7 @@ public class StrategyDirectory: NSObject, StrategyDirectoryProtocol { proteusProvider: ProteusProviding, mlsService: MLSServiceInterface, coreCryptoProvider: CoreCryptoProviderProtocol, + pullSelfUserClientsFactory: @escaping PullSelfUserClientsFactory, searchUsersCache: SearchUsersCache? ) { @@ -71,6 +72,7 @@ public class StrategyDirectory: NSObject, StrategyDirectoryProtocol { proteusProvider: proteusProvider, mlsService: mlsService, coreCryptoProvider: coreCryptoProvider, + pullSelfUserClientsFactory: pullSelfUserClientsFactory, searchUsersCache: searchUsersCache ) @@ -110,6 +112,7 @@ public class StrategyDirectory: NSObject, StrategyDirectoryProtocol { proteusProvider: ProteusProviding, mlsService: MLSServiceInterface, coreCryptoProvider: CoreCryptoProviderProtocol, + pullSelfUserClientsFactory: @escaping PullSelfUserClientsFactory, searchUsersCache: SearchUsersCache? ) -> [Any] { let syncMOC = contextProvider.syncContext @@ -382,7 +385,8 @@ public class StrategyDirectory: NSObject, StrategyDirectoryProtocol { clientUpdateStatus: applicationStatusDirectory.clientUpdateStatus, resolveOneOnOneConversations: makeResolveOneOnOneConversationsUseCase( context: syncMOC, - resolver: oneOnOneResolver + resolver: oneOnOneResolver, + pullSelfUserClientsFactory: pullSelfUserClientsFactory ) ), ResetSessionRequestStrategy( @@ -417,12 +421,15 @@ public class StrategyDirectory: NSObject, StrategyDirectoryProtocol { private static func makeResolveOneOnOneConversationsUseCase( context: NSManagedObjectContext, - resolver: any OneOnOneResolverInterface + resolver: any OneOnOneResolverInterface, + pullSelfUserClientsFactory: @escaping PullSelfUserClientsFactory ) -> any ResolveOneOnOneConversationsUseCaseProtocol { + ResolveOneOnOneConversationsUseCase( context: context, supportedProtocolService: SupportedProtocolsService(context: context), - resolver: resolver + resolver: resolver, + pullSelfUserClientsFactory: pullSelfUserClientsFactory ) } } diff --git a/wire-ios-sync-engine/Source/Use cases/ResolveOneOnOneConversationsUseCase.swift b/wire-ios-sync-engine/Source/Use cases/ResolveOneOnOneConversationsUseCase.swift index 684e360fa5..c660fd4c8c 100644 --- a/wire-ios-sync-engine/Source/Use cases/ResolveOneOnOneConversationsUseCase.swift +++ b/wire-ios-sync-engine/Source/Use cases/ResolveOneOnOneConversationsUseCase.swift @@ -17,6 +17,8 @@ // import Foundation +import WireDomain +import WireLogging // sourcery: AutoMockable public protocol ResolveOneOnOneConversationsUseCaseProtocol { @@ -25,32 +27,45 @@ public protocol ResolveOneOnOneConversationsUseCaseProtocol { } +typealias PullSelfUserClientsFactory = (NSManagedObjectContext) -> PullSelfUserClients + struct ResolveOneOnOneConversationsUseCase: ResolveOneOnOneConversationsUseCaseProtocol { let context: NSManagedObjectContext let supportedProtocolService: any SupportedProtocolsServiceInterface let resolver: any OneOnOneResolverInterface - + let pullSelfUserClientsFactory: PullSelfUserClientsFactory + func invoke() async throws { - let (oldProtocols, newProtocols) = await context.perform { + let oldProtocols = await context.perform { let selfUser = ZMUser.selfUser(in: context) - let oldProtocols = selfUser.supportedProtocols - let newProtocols = supportedProtocolService.calculateSupportedProtocols() - return (oldProtocols, newProtocols) + return selfUser.supportedProtocols } - + + let newProtocols = await calculateSupportedProtocols() if oldProtocols != newProtocols { var action = PushSupportedProtocolsAction(supportedProtocols: newProtocols) try await action.perform(in: context.notificationContext) - + await context.perform { let selfUser = ZMUser.selfUser(in: context) selfUser.supportedProtocols = newProtocols } } - + if newProtocols.contains(.mls) { try await resolver.resolveAllOneOnOneConversations(in: context) } } + + private func calculateSupportedProtocols() async -> Set { + // we need the self clients to be up to date before calculating supported protocols + let pullSelfUserClients = pullSelfUserClientsFactory(context) + do { + try await pullSelfUserClients.pullSelfClients() + } catch { + WireLogger.userClient.error("error syncing selfclients: \(error.localizedDescription)") + } + return await context.perform { supportedProtocolService.calculateSupportedProtocols() } + } } diff --git a/wire-ios-sync-engine/Source/UserSession/ZMUserSession/ZMUserSession.swift b/wire-ios-sync-engine/Source/UserSession/ZMUserSession/ZMUserSession.swift index 5b0ba3fdb2..d35a22a7c5 100644 --- a/wire-ios-sync-engine/Source/UserSession/ZMUserSession/ZMUserSession.swift +++ b/wire-ios-sync-engine/Source/UserSession/ZMUserSession/ZMUserSession.swift @@ -24,6 +24,8 @@ import WireDataModel import WireLogging import WireRequestStrategy import WireSystem +import WireDomain +import WireDomainPkg typealias UserSessionDelegate = UserSessionAppLockDelegate & UserSessionEncryptionAtRestDelegate @@ -576,10 +578,13 @@ public final class ZMUserSession: NSObject { proteusProvider: proteusProvider, mlsService: mlsService, coreCryptoProvider: coreCryptoProvider, + pullSelfUserClientsFactory: pullSelfUserClientsFactory, searchUsersCache: dependencies.caches.searchUsers ) } + + private func createUpdateEventProcessor() -> EventProcessor { EventProcessor( storeProvider: coreDataStack, @@ -998,19 +1003,40 @@ extension ZMUserSession: ZMSyncStateDelegate { } private func makeResolveOneOnOneConversationsUseCase(context: NSManagedObjectContext) - -> any ResolveOneOnOneConversationsUseCaseProtocol { + -> any ResolveOneOnOneConversationsUseCaseProtocol { let supportedProtocolService = SupportedProtocolsService(context: context) + + let resolver = OneOnOneResolver( migrator: OneOnOneMigrator(mlsService: mlsService), isMLSEnabled: mlsFeature.isEnabled ) - - return ResolveOneOnOneConversationsUseCase( - context: context, - supportedProtocolService: supportedProtocolService, - resolver: resolver + + + return ResolveOneOnOneConversationsUseCase(context: context, + supportedProtocolService: supportedProtocolService, + resolver: resolver, + pullSelfUserClientsFactory: pullSelfUserClientsFactory ) } + + + private func pullSelfUserClientsFactory(context: NSManagedObjectContext) -> PullSelfUserClients { + guard let apiService = managedObjectContext.performAndWait({ self.apiService }) else { + fatal("cannot initialize ResolveOneOnOneConversationsUseCase") + } + guard let apiVersion = BackendInfo.apiVersion, + let wireAPIVersion = WireAPI.APIVersion(rawValue: UInt(apiVersion.rawValue)) else { + WireLogger.backend.warn("apiVersion not resolved") + + fatal("cannot initialize ResolveOneOnOneConversationsUseCase") + + } + + return PullSelfUserClients.make(apiService: apiService, + apiVersion: wireAPIVersion, + context: context) + } private func resolveOneOnOneConversationsIfNeeded() async { guard mlsFeature.isEnabled else { return } From 2310e3cdcb1f8311c7b65bf6daccc54b986f2403 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franc=CC=A7ois=20Benaiteau?= Date: Mon, 13 Jan 2025 15:20:39 +0100 Subject: [PATCH 03/11] set anta backnd --- .../xcshareddata/xcschemes/Wire-iOS.xcscheme | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wire-ios/Wire-iOS.xcodeproj/xcshareddata/xcschemes/Wire-iOS.xcscheme b/wire-ios/Wire-iOS.xcodeproj/xcshareddata/xcschemes/Wire-iOS.xcscheme index 27e9287c6b..a0e8ab71ba 100644 --- a/wire-ios/Wire-iOS.xcodeproj/xcshareddata/xcschemes/Wire-iOS.xcscheme +++ b/wire-ios/Wire-iOS.xcodeproj/xcshareddata/xcschemes/Wire-iOS.xcscheme @@ -141,8 +141,8 @@ isEnabled = "NO"> + argument = "-BackendEnvironmentTypeOverrideKey anta" + isEnabled = "YES"> Date: Mon, 13 Jan 2025 17:04:42 +0100 Subject: [PATCH 04/11] fix tests --- .../UseCases/PullSelfUserClients.swift | 18 ++- .../generated/AutoMockable.generated.swift | 29 +++++ .../UseCases/PullSelfUserClientsTests.swift | 111 ++++++++++++++++++ .../ResolveOneOnOneConversationsUseCase.swift | 2 +- .../ZMUserSession/ZMUserSession.swift | 2 +- ...lveOneOnOneConversationsUseCaseTests.swift | 28 ++++- 6 files changed, 183 insertions(+), 7 deletions(-) create mode 100644 WireDomain/Tests/WireDomainTests/UseCases/PullSelfUserClientsTests.swift diff --git a/WireDomain/Sources/WireDomain/UseCases/PullSelfUserClients.swift b/WireDomain/Sources/WireDomain/UseCases/PullSelfUserClients.swift index 32a39be7d1..aaaffaa4ac 100644 --- a/WireDomain/Sources/WireDomain/UseCases/PullSelfUserClients.swift +++ b/WireDomain/Sources/WireDomain/UseCases/PullSelfUserClients.swift @@ -19,10 +19,22 @@ import WireAPI import CoreData -public struct PullSelfUserClients { +// sourcery: AutoMockable +public protocol PullSelfUserClientsProtocol { + + func pullSelfClients() async throws +} + +/// Pull self clients from backend and update local state +public struct PullSelfUserClients: PullSelfUserClientsProtocol { private let userClientsAPI: any UserClientsAPI private let userClientsLocalStore: any UserClientsLocalStoreProtocol + init(userClientsAPI: any UserClientsAPI, userClientsLocalStore: any UserClientsLocalStoreProtocol) { + self.userClientsAPI = userClientsAPI + self.userClientsLocalStore = userClientsLocalStore + } + public func pullSelfClients() async throws { let remoteSelfClients = try await userClientsAPI.getSelfClients() @@ -66,8 +78,8 @@ public struct PullSelfUserClients { public extension PullSelfUserClients { static func make(apiService: any APIServiceProtocol, - apiVersion: WireAPI.APIVersion, - context: NSManagedObjectContext) -> PullSelfUserClients { + apiVersion: WireAPI.APIVersion, + context: NSManagedObjectContext) -> PullSelfUserClientsProtocol { let userClientsAPI = UserClientsAPIBuilder(apiService: apiService).makeAPI(for: apiVersion) let userLocalStore = UserLocalStore(context: context) diff --git a/WireDomain/Sources/WireDomainSupport/Sourcery/generated/AutoMockable.generated.swift b/WireDomain/Sources/WireDomainSupport/Sourcery/generated/AutoMockable.generated.swift index 9d5a5b7dbf..2d2a6e03de 100644 --- a/WireDomain/Sources/WireDomainSupport/Sourcery/generated/AutoMockable.generated.swift +++ b/WireDomain/Sources/WireDomainSupport/Sourcery/generated/AutoMockable.generated.swift @@ -1507,6 +1507,35 @@ class MockProteusMessageDecryptorProtocol: ProteusMessageDecryptorProtocol { } +public class MockPullSelfUserClientsProtocol: PullSelfUserClientsProtocol { + + // MARK: - Life cycle + + public init() {} + + + // MARK: - pullSelfClients + + public var pullSelfClients_Invocations: [Void] = [] + public var pullSelfClients_MockError: Error? + public var pullSelfClients_MockMethod: (() async throws -> Void)? + + public func pullSelfClients() async throws { + pullSelfClients_Invocations.append(()) + + if let error = pullSelfClients_MockError { + throw error + } + + guard let mock = pullSelfClients_MockMethod else { + fatalError("no mock for `pullSelfClients`") + } + + try await mock() + } + +} + public class MockPushSupportedProtocolsUseCaseProtocol: PushSupportedProtocolsUseCaseProtocol { // MARK: - Life cycle diff --git a/WireDomain/Tests/WireDomainTests/UseCases/PullSelfUserClientsTests.swift b/WireDomain/Tests/WireDomainTests/UseCases/PullSelfUserClientsTests.swift new file mode 100644 index 0000000000..e969efa65c --- /dev/null +++ b/WireDomain/Tests/WireDomainTests/UseCases/PullSelfUserClientsTests.swift @@ -0,0 +1,111 @@ +// +// Wire +// Copyright (C) 2025 Wire Swiss GmbH +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see http://www.gnu.org/licenses/. +// + +import WireAPISupport +import WireDataModel +import WireDataModelSupport +import WireDomainSupport +import WireTestingPackage +import XCTest +@testable import WireAPI +@testable import WireDomain + +final class PullSelfUserClientsTests: XCTestCase { + + private var sut: PullSelfUserClients! + private var userClientsAPI: MockUserClientsAPI! + private var userClientsLocalStore: MockUserClientsLocalStoreProtocol! + private var stack: CoreDataStack! + private var coreDataStackHelper: CoreDataStackHelper! + private var modelHelper: ModelHelper! + + private var context: NSManagedObjectContext { + stack.syncContext + } + + override func setUp() async throws { + coreDataStackHelper = CoreDataStackHelper() + modelHelper = ModelHelper() + stack = try await coreDataStackHelper.createStack() + userClientsAPI = MockUserClientsAPI() + userClientsLocalStore = MockUserClientsLocalStoreProtocol() + + sut = PullSelfUserClients( + userClientsAPI: userClientsAPI, + userClientsLocalStore: userClientsLocalStore + ) + } + + override func tearDown() async throws { + stack = nil + userClientsAPI = nil + sut = nil + try coreDataStackHelper.cleanupDirectory() + coreDataStackHelper = nil + modelHelper = nil + } + + // MARK: - Tests + + func testPullSelfClients_It_Invokes_Local_Store_And_User_Repo_Methods() async throws { + // Mock + + let selfUserClient = await context.perform { [self] in + return modelHelper.createSelfClient( + id: Scaffolding.otherUserClientID, + in: context + ) + } + + userClientsAPI.getSelfClients_MockValue = [ + Scaffolding.selfUserClient + ] + + userClientsLocalStore.fetchOrCreateClientId_MockValue = (selfUserClient, false) + userClientsLocalStore.updateClientIdIsNewClientUserClientInfo_MockMethod = { _, _, _ in } + userClientsLocalStore.deletedSelfClientsNewClients_MockValue = [Scaffolding.userClientID] + userClientsLocalStore.deleteClientId_MockMethod = { _ in } + + // When + + try await sut.pullSelfClients() + + // Then + + XCTAssertEqual(userClientsLocalStore.fetchOrCreateClientId_Invocations.count, 1) + XCTAssertEqual(userClientsLocalStore.updateClientIdIsNewClientUserClientInfo_Invocations.count, 1) + XCTAssertEqual(userClientsLocalStore.deletedSelfClientsNewClients_Invocations.count, 1) + XCTAssertEqual(userClientsLocalStore.deleteClientId_Invocations.count, 1) + } + + private enum Scaffolding { + static let userClientID = UUID.mockID1.uuidString + static let otherUserClientID = UUID.mockID2.uuidString + + static let selfUserClient = WireAPI.SelfUserClient( + id: userClientID, + type: .permanent, + activationDate: .now, + label: "test", + model: "test", + deviceClass: .phone, + capabilities: [] + ) + } + +} diff --git a/wire-ios-sync-engine/Source/Use cases/ResolveOneOnOneConversationsUseCase.swift b/wire-ios-sync-engine/Source/Use cases/ResolveOneOnOneConversationsUseCase.swift index c660fd4c8c..94c4ec9a23 100644 --- a/wire-ios-sync-engine/Source/Use cases/ResolveOneOnOneConversationsUseCase.swift +++ b/wire-ios-sync-engine/Source/Use cases/ResolveOneOnOneConversationsUseCase.swift @@ -27,7 +27,7 @@ public protocol ResolveOneOnOneConversationsUseCaseProtocol { } -typealias PullSelfUserClientsFactory = (NSManagedObjectContext) -> PullSelfUserClients +typealias PullSelfUserClientsFactory = (NSManagedObjectContext) -> PullSelfUserClientsProtocol struct ResolveOneOnOneConversationsUseCase: ResolveOneOnOneConversationsUseCaseProtocol { diff --git a/wire-ios-sync-engine/Source/UserSession/ZMUserSession/ZMUserSession.swift b/wire-ios-sync-engine/Source/UserSession/ZMUserSession/ZMUserSession.swift index d35a22a7c5..2d9b277b4a 100644 --- a/wire-ios-sync-engine/Source/UserSession/ZMUserSession/ZMUserSession.swift +++ b/wire-ios-sync-engine/Source/UserSession/ZMUserSession/ZMUserSession.swift @@ -1021,7 +1021,7 @@ extension ZMUserSession: ZMSyncStateDelegate { } - private func pullSelfUserClientsFactory(context: NSManagedObjectContext) -> PullSelfUserClients { + private func pullSelfUserClientsFactory(context: NSManagedObjectContext) -> PullSelfUserClientsProtocol { guard let apiService = managedObjectContext.performAndWait({ self.apiService }) else { fatal("cannot initialize ResolveOneOnOneConversationsUseCase") } diff --git a/wire-ios-sync-engine/Tests/Source/Use cases/ResolveOneOnOneConversationsUseCaseTests.swift b/wire-ios-sync-engine/Tests/Source/Use cases/ResolveOneOnOneConversationsUseCaseTests.swift index 4eb6548f5b..28fe27b3bc 100644 --- a/wire-ios-sync-engine/Tests/Source/Use cases/ResolveOneOnOneConversationsUseCaseTests.swift +++ b/wire-ios-sync-engine/Tests/Source/Use cases/ResolveOneOnOneConversationsUseCaseTests.swift @@ -20,6 +20,7 @@ import WireDataModelSupport import WireSyncEngineSupport import XCTest @testable import WireSyncEngine +import WireDomainSupport final class ResolveOneOnOneConversationsUseCaseTests: XCTestCase { @@ -28,6 +29,7 @@ final class ResolveOneOnOneConversationsUseCaseTests: XCTestCase { private var sut: ResolveOneOnOneConversationsUseCase! private var mockSupportedProtocolService: MockSupportedProtocolsServiceInterface! private var mockOneOnOneResolver: MockOneOnOneResolverInterface! + private var mockPullSelfUserClients: MockPullSelfUserClientsProtocol! private var stack: CoreDataStack! private let coreDataStackHelper = CoreDataStackHelper() @@ -42,11 +44,16 @@ final class ResolveOneOnOneConversationsUseCaseTests: XCTestCase { stack = try await coreDataStackHelper.createStack() mockSupportedProtocolService = MockSupportedProtocolsServiceInterface() mockOneOnOneResolver = MockOneOnOneResolverInterface() - + mockPullSelfUserClients = MockPullSelfUserClientsProtocol() + mockPullSelfUserClients.pullSelfClients_MockMethod = {} + sut = ResolveOneOnOneConversationsUseCase( context: syncContext, supportedProtocolService: mockSupportedProtocolService, - resolver: mockOneOnOneResolver + resolver: mockOneOnOneResolver, + pullSelfUserClientsFactory: { _ in + return self.mockPullSelfUserClients + } ) } @@ -56,6 +63,7 @@ final class ResolveOneOnOneConversationsUseCaseTests: XCTestCase { stack = nil mockSupportedProtocolService = nil mockOneOnOneResolver = nil + mockPullSelfUserClients = nil sut = nil try coreDataStackHelper.cleanupDirectory() try await super.tearDown() @@ -63,6 +71,22 @@ final class ResolveOneOnOneConversationsUseCaseTests: XCTestCase { // MARK: - Unit Tests + func test_invoke_Calls_PullSelfClients() async throws { + // GIVEN + await syncContext.perform { [self] in + let selfUser = ZMUser.selfUser(in: syncContext) + selfUser.supportedProtocols = [.proteus] + mockSupportedProtocolService.calculateSupportedProtocols_MockValue = [.mls, .proteus] + } + + // WHEN + try await sut.invoke() + + // THEN + XCTAssertEqual(mockPullSelfUserClients.pullSelfClients_Invocations.count, 1) + } + + func test_SupportedProtocolsRemainProteusOnly() async throws { // GIVEN await syncContext.perform { [self] in From ebb9a0d03e83b2ffd93cec1dbf6f30bc0b173942 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franc=CC=A7ois=20Benaiteau?= Date: Mon, 13 Jan 2025 17:16:07 +0100 Subject: [PATCH 05/11] revert SupportedProtocolsHelper --- .../PushSupportedProtocolsUseCase.swift | 41 ++++--------------- 1 file changed, 9 insertions(+), 32 deletions(-) diff --git a/WireDomain/Sources/WireDomain/UseCases/PushSupportedProtocolsUseCase.swift b/WireDomain/Sources/WireDomain/UseCases/PushSupportedProtocolsUseCase.swift index aa31916a8b..47a7baa2fb 100644 --- a/WireDomain/Sources/WireDomain/UseCases/PushSupportedProtocolsUseCase.swift +++ b/WireDomain/Sources/WireDomain/UseCases/PushSupportedProtocolsUseCase.swift @@ -27,7 +27,8 @@ public protocol PushSupportedProtocolsUseCaseProtocol { func invoke() async throws } -public struct SupportedProtocolsHelper { +public struct PushSupportedProtocolsUseCase: PushSupportedProtocolsUseCaseProtocol { + private enum ProteusToMLSMigrationState: String { case disabled case notStarted @@ -36,11 +37,17 @@ public struct SupportedProtocolsHelper { } let featureConfigRepository: any FeatureConfigRepositoryProtocol + let userRepository: any UserRepositoryProtocol let userClientsRepository: any UserClientsRepositoryProtocol private let logger = WireLogger(tag: "supported-protocols") - public func calculateSupportedProtocols() async -> Set { + public func invoke() async throws { + let supportedProtocols = await calculateSupportedProtocols() + try await userRepository.pushSelfSupportedProtocols(supportedProtocols) + } + + private func calculateSupportedProtocols() async -> Set { logger.debug("calculating supported protocols...") let remoteProtocols = await remotelySupportedProtocols() @@ -154,35 +161,5 @@ public struct SupportedProtocolsHelper { private func allSelfUserClientsAreActiveMLSClients() async -> Bool { await userClientsRepository.allSelfUserClientsAreActiveMLSClients() } -} - -public struct PushSupportedProtocolsUseCase: PushSupportedProtocolsUseCaseProtocol { - - private enum ProteusToMLSMigrationState: String { - case disabled - case notStarted - case ongoing - case finalised - } - - let helper: SupportedProtocolsHelper - let userRepository: any UserRepositoryProtocol - - private let logger = WireLogger(tag: "supported-protocols") - - init(featureConfigRepository: any FeatureConfigRepositoryProtocol, - userRepository: any UserRepositoryProtocol, - userClientsRepository: any UserClientsRepositoryProtocol - ) { - - self.helper = SupportedProtocolsHelper(featureConfigRepository: featureConfigRepository, - userClientsRepository: userClientsRepository) - self.userRepository = userRepository - } - - public func invoke() async throws { - let supportedProtocols = await helper.calculateSupportedProtocols() - try await userRepository.pushSelfSupportedProtocols(supportedProtocols) - } } From 746c4506e9de1bcd44592623ac9b0f70aad9827d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franc=CC=A7ois=20Benaiteau?= Date: Mon, 13 Jan 2025 17:16:19 +0100 Subject: [PATCH 06/11] cleanup --- .../UserClients/UserClientsRepository.swift | 59 ------------------- 1 file changed, 59 deletions(-) diff --git a/WireDomain/Sources/WireDomain/Repositories/UserClients/UserClientsRepository.swift b/WireDomain/Sources/WireDomain/Repositories/UserClients/UserClientsRepository.swift index 1020f61751..06f86f7ba0 100644 --- a/WireDomain/Sources/WireDomain/Repositories/UserClients/UserClientsRepository.swift +++ b/WireDomain/Sources/WireDomain/Repositories/UserClients/UserClientsRepository.swift @@ -113,62 +113,3 @@ public struct UserClientsRepository: UserClientsRepositoryProtocol { } } - - - - - - - -//public extension UserClientsRepository { -// -// static func make(apiService: any APIServiceProtocol, -// apiVersion: WireAPI.APIVersion, -// context: NSManagedObjectContext) -> UserClientsRepositoryProtocol { -// -// let userClientsAPI = UserClientsAPIBuilder(apiService: apiService).makeAPI(for: apiVersion) -// let usersAPI = UsersAPIBuilder(apiService: apiService).makeAPI(for: apiVersion) -// let selfUserAPI = SelfUserAPIBuilder(apiService: apiService).makeAPI(for: apiVersion) -// let userPropertiesAPI = UserPropertiesBuilder(apiService: apiService).makeAPI(for: apiVersion) -// -// -// let conversationLabelsLocalStore = ConversationLabelsLocalStore(context: context) -// -// let conversationRepository = ConversationRepository(conversationsAPI: <#T##any ConversationsAPI#>, -// conversationsLocalStore: any ConversationLocalStoreProtocol, -// userRepository: <#T##any UserRepositoryProtocol#>, -// teamRepository: <#T##any TeamRepositoryProtocol#>, -// messageRepository: <#T##any MessageRepositoryProtocol#>, -// backendInfo: <#T##ConversationRepository.BackendInfo#>, -// mlsProvider: <#T##MLSProvider#>) -// -// let conversationLabelsRepository = ConversationLabelsRepository(userPropertiesAPI: userPropertiesAPI, conversationLabelsLocalStore: conversationLabelsLocalStore) -// -// -// let userRepository = UserRepository(usersAPI: usersAPI, -// selfUserAPI: selfUserAPI, -// conversationLabelsRepository: conversationLabelsRepository, conversationRepository: <#T##any ConversationRepositoryProtocol#>, userLocalStore: <#T##any UserLocalStoreProtocol#>) -// let userClientsLocalStore = UserClientsLocalStore(context: context, userLocalStore: ) -// -// return UserClientsRepository(userClientsAPI: userClientsAPI, -// userRepository: userRepository, -// userClientsLocalStore: userClientsLocalStore) -// } -//} - -//public extension SupportedProtocolsHelper { -// -// public static func make(apiService: any APIServiceProtocol, apiVersion: WireAPI.APIVersion, context: NSManagedObjectContext) -> SupportedProtocolsHelper { -// -// let userClientsRepository = UserClientsRepository.make(apiService: apiService, apiVersion: apiVersion, context: context) -// -// let featureConfigRepository = FeatureConfigRepository( -// featureConfigsAPI: FeatureConfigsAPIBuilder(apiService: apiService).makeAPI(for: apiVersion), -// featureConfigLocalStore: FeatureConfigLocalStore(context: context) -// ) -// -// -// return SupportedProtocolsHelper(featureConfigRepository: featureConfigRepository, -// userClientsRepository: <#T##any UserClientsRepositoryProtocol#>) -// } -//} From c0c6e066850dc8acb2914dad55dd5c7ffc6b0d3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franc=CC=A7ois=20Benaiteau?= Date: Mon, 13 Jan 2025 17:16:31 +0100 Subject: [PATCH 07/11] cleanup --- .../Source/UserSession/ZMUserSession/ZMUserSession.swift | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/wire-ios-sync-engine/Source/UserSession/ZMUserSession/ZMUserSession.swift b/wire-ios-sync-engine/Source/UserSession/ZMUserSession/ZMUserSession.swift index 2d9b277b4a..c23b89f62f 100644 --- a/wire-ios-sync-engine/Source/UserSession/ZMUserSession/ZMUserSession.swift +++ b/wire-ios-sync-engine/Source/UserSession/ZMUserSession/ZMUserSession.swift @@ -21,11 +21,10 @@ import Foundation import WireAnalytics import WireAPI import WireDataModel +import WireDomain import WireLogging import WireRequestStrategy import WireSystem -import WireDomain -import WireDomainPkg typealias UserSessionDelegate = UserSessionAppLockDelegate & UserSessionEncryptionAtRestDelegate From 55b023d7ba6411f7052dd9b90342ddc89eb3d8d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franc=CC=A7ois=20Benaiteau?= Date: Mon, 13 Jan 2025 17:27:59 +0100 Subject: [PATCH 08/11] fix --- .../Sourcery/generated/AutoMockable.generated.swift | 2 +- .../ResolveOneOnOneConversationsUseCaseTests.swift | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/WireDomain/Sources/WireDomainSupport/Sourcery/generated/AutoMockable.generated.swift b/WireDomain/Sources/WireDomainSupport/Sourcery/generated/AutoMockable.generated.swift index 2d2a6e03de..eb46951aa4 100644 --- a/WireDomain/Sources/WireDomainSupport/Sourcery/generated/AutoMockable.generated.swift +++ b/WireDomain/Sources/WireDomainSupport/Sourcery/generated/AutoMockable.generated.swift @@ -1100,7 +1100,7 @@ public class MockConversationRepositoryProtocol: ConversationRepositoryProtocol guard let mock = addParticipantsSenderDateConversationIDConversationDomain_MockMethod else { fatalError("no mock for `addParticipantsSenderDateConversationIDConversationDomain`") } - + try await mock(participants, sender, date, conversationID, conversationDomain) } diff --git a/wire-ios-sync-engine/Tests/Source/Use cases/ResolveOneOnOneConversationsUseCaseTests.swift b/wire-ios-sync-engine/Tests/Source/Use cases/ResolveOneOnOneConversationsUseCaseTests.swift index 28fe27b3bc..c42aaaf8a3 100644 --- a/wire-ios-sync-engine/Tests/Source/Use cases/ResolveOneOnOneConversationsUseCaseTests.swift +++ b/wire-ios-sync-engine/Tests/Source/Use cases/ResolveOneOnOneConversationsUseCaseTests.swift @@ -46,7 +46,7 @@ final class ResolveOneOnOneConversationsUseCaseTests: XCTestCase { mockOneOnOneResolver = MockOneOnOneResolverInterface() mockPullSelfUserClients = MockPullSelfUserClientsProtocol() mockPullSelfUserClients.pullSelfClients_MockMethod = {} - + sut = ResolveOneOnOneConversationsUseCase( context: syncContext, supportedProtocolService: mockSupportedProtocolService, @@ -75,15 +75,17 @@ final class ResolveOneOnOneConversationsUseCaseTests: XCTestCase { // GIVEN await syncContext.perform { [self] in let selfUser = ZMUser.selfUser(in: syncContext) - selfUser.supportedProtocols = [.proteus] - mockSupportedProtocolService.calculateSupportedProtocols_MockValue = [.mls, .proteus] + selfUser.supportedProtocols = [.mls] + mockSupportedProtocolService.calculateSupportedProtocols_MockValue = [.mls] } + mockOneOnOneResolver.resolveAllOneOnOneConversationsIn_MockMethod = { _ in } // WHEN try await sut.invoke() // THEN XCTAssertEqual(mockPullSelfUserClients.pullSelfClients_Invocations.count, 1) + XCTAssertEqual(mockOneOnOneResolver.resolveAllOneOnOneConversationsIn_Invocations.count, 1) } From 18b39070300e760a9f42c6527a26528458b550fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franc=CC=A7ois=20Benaiteau?= Date: Mon, 13 Jan 2025 17:29:51 +0100 Subject: [PATCH 09/11] Revert "set anta backnd" 2310e3cdcb1f8311c7b65bf6daccc54b986f2403 --- .../xcshareddata/xcschemes/Wire-iOS.xcscheme | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wire-ios/Wire-iOS.xcodeproj/xcshareddata/xcschemes/Wire-iOS.xcscheme b/wire-ios/Wire-iOS.xcodeproj/xcshareddata/xcschemes/Wire-iOS.xcscheme index a0e8ab71ba..27e9287c6b 100644 --- a/wire-ios/Wire-iOS.xcodeproj/xcshareddata/xcschemes/Wire-iOS.xcscheme +++ b/wire-ios/Wire-iOS.xcodeproj/xcshareddata/xcschemes/Wire-iOS.xcscheme @@ -141,8 +141,8 @@ isEnabled = "NO"> + argument = "-BackendEnvironmentTypeOverrideKey staging" + isEnabled = "NO"> Date: Mon, 13 Jan 2025 21:05:28 +0100 Subject: [PATCH 10/11] format --- .../UseCases/PullSelfUserClients.swift | 24 ++++++------ .../Synchronization/StrategyDirectory.swift | 2 +- .../ResolveOneOnOneConversationsUseCase.swift | 10 ++--- .../ZMUserSession/ZMUserSession.swift | 38 +++++++++---------- ...lveOneOnOneConversationsUseCaseTests.swift | 11 +++--- 5 files changed, 41 insertions(+), 44 deletions(-) diff --git a/WireDomain/Sources/WireDomain/UseCases/PullSelfUserClients.swift b/WireDomain/Sources/WireDomain/UseCases/PullSelfUserClients.swift index aaaffaa4ac..a301d30235 100644 --- a/WireDomain/Sources/WireDomain/UseCases/PullSelfUserClients.swift +++ b/WireDomain/Sources/WireDomain/UseCases/PullSelfUserClients.swift @@ -16,12 +16,12 @@ // along with this program. If not, see http://www.gnu.org/licenses/. // -import WireAPI import CoreData +import WireAPI // sourcery: AutoMockable public protocol PullSelfUserClientsProtocol { - + func pullSelfClients() async throws } @@ -34,7 +34,7 @@ public struct PullSelfUserClients: PullSelfUserClientsProtocol { self.userClientsAPI = userClientsAPI self.userClientsLocalStore = userClientsLocalStore } - + public func pullSelfClients() async throws { let remoteSelfClients = try await userClientsAPI.getSelfClients() @@ -58,8 +58,7 @@ public struct PullSelfUserClients: PullSelfUserClientsProtocol { await userClientsLocalStore.deleteClient(id: deletedSelfClientID) } } - - + func updateClient( id: String, from remoteClient: WireAPI.SelfUserClient, @@ -72,19 +71,20 @@ public struct PullSelfUserClients: PullSelfUserClientsProtocol { ) } - } public extension PullSelfUserClients { - - static func make(apiService: any APIServiceProtocol, - apiVersion: WireAPI.APIVersion, - context: NSManagedObjectContext) -> PullSelfUserClientsProtocol { + + static func make( + apiService: any APIServiceProtocol, + apiVersion: WireAPI.APIVersion, + context: NSManagedObjectContext + ) -> PullSelfUserClientsProtocol { let userClientsAPI = UserClientsAPIBuilder(apiService: apiService).makeAPI(for: apiVersion) - + let userLocalStore = UserLocalStore(context: context) let userClientsLocalStore = UserClientsLocalStore(context: context, userLocalStore: userLocalStore) - + return PullSelfUserClients(userClientsAPI: userClientsAPI, userClientsLocalStore: userClientsLocalStore) } } diff --git a/wire-ios-sync-engine/Source/Synchronization/StrategyDirectory.swift b/wire-ios-sync-engine/Source/Synchronization/StrategyDirectory.swift index cdb390124a..7ae463d762 100644 --- a/wire-ios-sync-engine/Source/Synchronization/StrategyDirectory.swift +++ b/wire-ios-sync-engine/Source/Synchronization/StrategyDirectory.swift @@ -424,7 +424,7 @@ public class StrategyDirectory: NSObject, StrategyDirectoryProtocol { resolver: any OneOnOneResolverInterface, pullSelfUserClientsFactory: @escaping PullSelfUserClientsFactory ) -> any ResolveOneOnOneConversationsUseCaseProtocol { - + ResolveOneOnOneConversationsUseCase( context: context, supportedProtocolService: SupportedProtocolsService(context: context), diff --git a/wire-ios-sync-engine/Source/Use cases/ResolveOneOnOneConversationsUseCase.swift b/wire-ios-sync-engine/Source/Use cases/ResolveOneOnOneConversationsUseCase.swift index 94c4ec9a23..a27f240950 100644 --- a/wire-ios-sync-engine/Source/Use cases/ResolveOneOnOneConversationsUseCase.swift +++ b/wire-ios-sync-engine/Source/Use cases/ResolveOneOnOneConversationsUseCase.swift @@ -35,29 +35,29 @@ struct ResolveOneOnOneConversationsUseCase: ResolveOneOnOneConversationsUseCaseP let supportedProtocolService: any SupportedProtocolsServiceInterface let resolver: any OneOnOneResolverInterface let pullSelfUserClientsFactory: PullSelfUserClientsFactory - + func invoke() async throws { let oldProtocols = await context.perform { let selfUser = ZMUser.selfUser(in: context) return selfUser.supportedProtocols } - + let newProtocols = await calculateSupportedProtocols() if oldProtocols != newProtocols { var action = PushSupportedProtocolsAction(supportedProtocols: newProtocols) try await action.perform(in: context.notificationContext) - + await context.perform { let selfUser = ZMUser.selfUser(in: context) selfUser.supportedProtocols = newProtocols } } - + if newProtocols.contains(.mls) { try await resolver.resolveAllOneOnOneConversations(in: context) } } - + private func calculateSupportedProtocols() async -> Set { // we need the self clients to be up to date before calculating supported protocols let pullSelfUserClients = pullSelfUserClientsFactory(context) diff --git a/wire-ios-sync-engine/Source/UserSession/ZMUserSession/ZMUserSession.swift b/wire-ios-sync-engine/Source/UserSession/ZMUserSession/ZMUserSession.swift index c23b89f62f..9b0891c7c4 100644 --- a/wire-ios-sync-engine/Source/UserSession/ZMUserSession/ZMUserSession.swift +++ b/wire-ios-sync-engine/Source/UserSession/ZMUserSession/ZMUserSession.swift @@ -582,8 +582,6 @@ public final class ZMUserSession: NSObject { ) } - - private func createUpdateEventProcessor() -> EventProcessor { EventProcessor( storeProvider: coreDataStack, @@ -1002,39 +1000,39 @@ extension ZMUserSession: ZMSyncStateDelegate { } private func makeResolveOneOnOneConversationsUseCase(context: NSManagedObjectContext) - -> any ResolveOneOnOneConversationsUseCaseProtocol { + -> any ResolveOneOnOneConversationsUseCaseProtocol { let supportedProtocolService = SupportedProtocolsService(context: context) - - + let resolver = OneOnOneResolver( migrator: OneOnOneMigrator(mlsService: mlsService), isMLSEnabled: mlsFeature.isEnabled ) - - - return ResolveOneOnOneConversationsUseCase(context: context, - supportedProtocolService: supportedProtocolService, - resolver: resolver, - pullSelfUserClientsFactory: pullSelfUserClientsFactory + + return ResolveOneOnOneConversationsUseCase( + context: context, + supportedProtocolService: supportedProtocolService, + resolver: resolver, + pullSelfUserClientsFactory: pullSelfUserClientsFactory ) } - - + private func pullSelfUserClientsFactory(context: NSManagedObjectContext) -> PullSelfUserClientsProtocol { - guard let apiService = managedObjectContext.performAndWait({ self.apiService }) else { + guard let apiService = managedObjectContext.performAndWait({ self.apiService }) else { fatal("cannot initialize ResolveOneOnOneConversationsUseCase") } guard let apiVersion = BackendInfo.apiVersion, let wireAPIVersion = WireAPI.APIVersion(rawValue: UInt(apiVersion.rawValue)) else { WireLogger.backend.warn("apiVersion not resolved") - + fatal("cannot initialize ResolveOneOnOneConversationsUseCase") - + } - - return PullSelfUserClients.make(apiService: apiService, - apiVersion: wireAPIVersion, - context: context) + + return PullSelfUserClients.make( + apiService: apiService, + apiVersion: wireAPIVersion, + context: context + ) } private func resolveOneOnOneConversationsIfNeeded() async { diff --git a/wire-ios-sync-engine/Tests/Source/Use cases/ResolveOneOnOneConversationsUseCaseTests.swift b/wire-ios-sync-engine/Tests/Source/Use cases/ResolveOneOnOneConversationsUseCaseTests.swift index c42aaaf8a3..a9e08d74e2 100644 --- a/wire-ios-sync-engine/Tests/Source/Use cases/ResolveOneOnOneConversationsUseCaseTests.swift +++ b/wire-ios-sync-engine/Tests/Source/Use cases/ResolveOneOnOneConversationsUseCaseTests.swift @@ -17,10 +17,10 @@ // import WireDataModelSupport +import WireDomainSupport import WireSyncEngineSupport import XCTest @testable import WireSyncEngine -import WireDomainSupport final class ResolveOneOnOneConversationsUseCaseTests: XCTestCase { @@ -46,13 +46,13 @@ final class ResolveOneOnOneConversationsUseCaseTests: XCTestCase { mockOneOnOneResolver = MockOneOnOneResolverInterface() mockPullSelfUserClients = MockPullSelfUserClientsProtocol() mockPullSelfUserClients.pullSelfClients_MockMethod = {} - + sut = ResolveOneOnOneConversationsUseCase( context: syncContext, supportedProtocolService: mockSupportedProtocolService, resolver: mockOneOnOneResolver, pullSelfUserClientsFactory: { _ in - return self.mockPullSelfUserClients + self.mockPullSelfUserClients } ) } @@ -82,13 +82,12 @@ final class ResolveOneOnOneConversationsUseCaseTests: XCTestCase { // WHEN try await sut.invoke() - + // THEN XCTAssertEqual(mockPullSelfUserClients.pullSelfClients_Invocations.count, 1) XCTAssertEqual(mockOneOnOneResolver.resolveAllOneOnOneConversationsIn_Invocations.count, 1) } - - + func test_SupportedProtocolsRemainProteusOnly() async throws { // GIVEN await syncContext.perform { [self] in From a71722f0ff319b52a8a1e060e02b9320e1830c5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franc=CC=A7ois=20Benaiteau?= Date: Tue, 14 Jan 2025 12:11:17 +0100 Subject: [PATCH 11/11] fix: protocol in different file --- .../UseCases/PullSelfUserClients.swift | 6 ----- .../PullSelfUserClientsProtocol.swift | 23 +++++++++++++++++++ 2 files changed, 23 insertions(+), 6 deletions(-) create mode 100644 WireDomain/Sources/WireDomain/UseCases/PullSelfUserClientsProtocol.swift diff --git a/WireDomain/Sources/WireDomain/UseCases/PullSelfUserClients.swift b/WireDomain/Sources/WireDomain/UseCases/PullSelfUserClients.swift index a301d30235..1159efbea2 100644 --- a/WireDomain/Sources/WireDomain/UseCases/PullSelfUserClients.swift +++ b/WireDomain/Sources/WireDomain/UseCases/PullSelfUserClients.swift @@ -19,12 +19,6 @@ import CoreData import WireAPI -// sourcery: AutoMockable -public protocol PullSelfUserClientsProtocol { - - func pullSelfClients() async throws -} - /// Pull self clients from backend and update local state public struct PullSelfUserClients: PullSelfUserClientsProtocol { private let userClientsAPI: any UserClientsAPI diff --git a/WireDomain/Sources/WireDomain/UseCases/PullSelfUserClientsProtocol.swift b/WireDomain/Sources/WireDomain/UseCases/PullSelfUserClientsProtocol.swift new file mode 100644 index 0000000000..d52e068bf1 --- /dev/null +++ b/WireDomain/Sources/WireDomain/UseCases/PullSelfUserClientsProtocol.swift @@ -0,0 +1,23 @@ +// +// Wire +// Copyright (C) 2025 Wire Swiss GmbH +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see http://www.gnu.org/licenses/. +// + +// sourcery: AutoMockable +public protocol PullSelfUserClientsProtocol { + + func pullSelfClients() async throws +}