Skip to content

Commit

Permalink
Create PendingInteraction model after configuration
Browse files Browse the repository at this point in the history
Make sure PendingInteraction model is created after configuration to prevent subscription for unread message count and pending SC status from failure, because 'pubsub' is created only when Core SDK is configured.

MOB-3871
  • Loading branch information
igorkravchenko committed Dec 12, 2024
1 parent e8b2a4a commit 76c87b1
Show file tree
Hide file tree
Showing 8 changed files with 106 additions and 30 deletions.
2 changes: 1 addition & 1 deletion GliaWidgets/Public/Glia/Glia+EngagementSetup.swift
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ extension Glia {
) {
let engagementLaunching: EngagementCoordinator.EngagementLaunching

switch (pendingInteraction.hasPendingInteraction, engagementKind) {
switch (pendingInteraction?.hasPendingInteraction ?? false, engagementKind) {
case (false, _):
// if there is no pending Secure Conversation, open regular flow.
engagementLaunching = .direct(kind: engagementKind)
Expand Down
2 changes: 1 addition & 1 deletion GliaWidgets/Public/Glia/Glia+EntryWidget.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ extension Glia {
isAuthenticated: environment.isAuthenticated,
hasPendingInteraction: { [weak self] in
guard let self else { return false }
return pendingInteraction.hasPendingInteraction
return pendingInteraction?.hasPendingInteraction ?? false
}
)
)
Expand Down
19 changes: 16 additions & 3 deletions GliaWidgets/Public/Glia/Glia.swift
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ public class Glia {
//
// Currently it's used to know if we have to force a visitor to SecureMessaging screen,
// once they try to start an engagement with media type other than `messaging`.
let pendingInteraction: SecureConversations.PendingInteraction
var pendingInteraction: SecureConversations.PendingInteraction?

init(environment: Environment) {
self.environment = environment
Expand Down Expand Up @@ -179,7 +179,6 @@ public class Glia {
viewFactory: viewFactory
)
)
pendingInteraction = .init(environment: .init(with: environment.coreSdk))
}

/// Setup SDK using specific engagement configuration without starting the engagement.
Expand Down Expand Up @@ -258,7 +257,21 @@ public class Glia {

// Configuration completion handler has to be called in any case,
// at the end of the scope, whether there's ongoing engagement or not.
defer { completion(.success(())) }
defer {
// PendingInteraction is essential part of SC flow, so it's not
// valid to consider SDK configured if PI is not created.
do {
pendingInteraction = try .init(environment: .init(with: environment.coreSdk))
completion(.success(()))
} catch let error as SecureConversations.PendingInteraction.Error {
switch error {
case .subscriptionFailure:
completion(.failure(GliaError.internalEventSubscriptionFailure))
}
} catch {
completion(.failure(GliaError.internalError))
}
}

guard let currentEngagement = self.environment.coreSdk.getCurrentEngagement() else { return }

Expand Down
3 changes: 3 additions & 0 deletions GliaWidgets/Public/GliaError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,7 @@ public enum GliaError: Error {

/// Internal error.
case internalError

/// Internal event subscription failure.
case internalEventSubscriptionFailure
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,34 @@ extension SecureConversations {
private(set) var pendingStatusCancellationToken: String?
private(set) var unreadMessageCountCancellationToken: String?

init(environment: Environment) {
init(environment: Environment) throws {
self.environment = environment
self.pendingStatusCancellationToken = environment.observePendingSecureConversationsStatus { [weak self] result in
let pendingStatusCancellationToken = environment.observePendingSecureConversationsStatus { [weak self] result in
guard let self else { return }
// At this point it is enough to know if there is a pending conversation,
// so no need to handle error.
pendingStatus = (try? result.get()) ?? false
}
self.unreadMessageCountCancellationToken = environment.observeSecureConversationsUnreadMessageCount { [weak self] result in

guard pendingStatusCancellationToken != nil else {
throw Error.subscriptionFailure(.pendingStatus)
}

self.pendingStatusCancellationToken = pendingStatusCancellationToken

let unreadMessageCountCancellationToken = environment.observeSecureConversationsUnreadMessageCount { [weak self] result in
guard let self else { return }
// At this point it is enough to know if there is an unread message count,
// so no need to handle error.
unreadMessageCount = (try? result.get()) ?? 0
}

guard unreadMessageCountCancellationToken != nil else {
throw Error.subscriptionFailure(.unreadMessageCount)
}

self.unreadMessageCountCancellationToken = unreadMessageCountCancellationToken

$pendingStatus.combineLatest($unreadMessageCount)
.map { hasPending, unreadCount in
hasPending || unreadCount > 0
Expand Down Expand Up @@ -52,6 +65,16 @@ extension SecureConversations.PendingInteraction {
}
}

extension SecureConversations.PendingInteraction {
enum Error: Swift.Error {
enum Subscription {
case unreadMessageCount
case pendingStatus
}
case subscriptionFailure(Subscription)
}
}

extension SecureConversations.PendingInteraction.Environment {
init(with client: CoreSdkClient) {
self.observePendingSecureConversationsStatus = client.observePendingSecureConversationStatus
Expand All @@ -63,17 +86,20 @@ extension SecureConversations.PendingInteraction.Environment {

#if DEBUG
extension SecureConversations.PendingInteraction.Environment {
static let mock = Self(
observePendingSecureConversationsStatus: { _ in nil },
observeSecureConversationsUnreadMessageCount: { _ in nil },
unsubscribeFromUnreadCount: { _ in },
unsubscribeFromPendingStatus: { _ in }
)
static let mock: Self = {
let uuidGen = UUID.incrementing
return Self(
observePendingSecureConversationsStatus: { _ in uuidGen().uuidString },
observeSecureConversationsUnreadMessageCount: { _ in uuidGen().uuidString },
unsubscribeFromUnreadCount: { _ in },
unsubscribeFromPendingStatus: { _ in }
)
}()
}

extension SecureConversations.PendingInteraction {
static func mock(environment: Environment = .mock) -> Self {
.init(environment: environment)
static func mock(environment: Environment = .mock) throws -> Self {
try .init(environment: environment)
}
}
#endif
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ extension SecureConversations.PendingInteraction.Environment {
}

extension SecureConversations.PendingInteraction {
static func failing() -> Self {
.init(environment: .failing)
static func failing() throws -> Self {
try .init(environment: .failing)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ final class SecureConversationsPendingInteractionTests: XCTestCase {
environment.unsubscribeFromPendingStatus = { _ in }
environment.unsubscribeFromUnreadCount = { _ in }

let pendingInteraction = SecureConversations.PendingInteraction(environment: environment)
let pendingInteraction = try SecureConversations.PendingInteraction(environment: environment)
// Assert initial pending interaction is false.
XCTAssertFalse(pendingInteraction.hasPendingInteraction)
// Affect pending secure conversations value by setting it to `true` and assert `hasPendingInteraction`,
Expand All @@ -36,7 +36,7 @@ final class SecureConversationsPendingInteractionTests: XCTestCase {
XCTAssertFalse(pendingInteraction.hasPendingInteraction)
}

func test_unsubscribeIsCalledOnDeinit() {
func test_unsubscribeIsCalledOnDeinit() throws {
enum Call {
case unsubscribeFromPendingStatus
case unsubscribeFromUnreadCount
Expand All @@ -52,8 +52,8 @@ final class SecureConversationsPendingInteractionTests: XCTestCase {
environment.unsubscribeFromUnreadCount = { _ in
calls.append(.unsubscribeFromUnreadCount)
}
var pendingInteraction = SecureConversations.PendingInteraction(environment: environment)
pendingInteraction = .mock()
var pendingInteraction = try SecureConversations.PendingInteraction(environment: environment)
pendingInteraction = try .mock()
_ = pendingInteraction
XCTAssertEqual(calls, [.unsubscribeFromUnreadCount, .unsubscribeFromPendingStatus])
}
Expand Down
50 changes: 42 additions & 8 deletions GliaWidgetsTests/Sources/Glia/GliaTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -747,10 +747,12 @@ final class GliaTests: XCTestCase {
XCTAssertEqual(messages, ["Initialize Glia Widgets SDK", "Setting Unified UI Config"])
}

func test_hasPendingInteractionIfPendingSecureConversationExists() {
func test_hasPendingInteractionIfPendingSecureConversationExists() throws {
var gliaEnv = Glia.Environment.failing
var logger = CoreSdkClient.Logger.failing
let uuidGen = UUID.incrementing
logger.infoClosure = { _, _, _, _ in }
logger.prefixedClosure = { _ in logger }
logger.configureLocalLogLevelClosure = { _ in }
logger.configureRemoteLogLevelClosure = { _ in }
gliaEnv.coreSdk.createLogger = { _ in logger }
Expand All @@ -765,18 +767,28 @@ final class GliaTests: XCTestCase {
}
gliaEnv.coreSdk.unsubscribeFromPendingSecureConversationStatus = { _ in }
gliaEnv.coreSdk.unsubscribeFromUnreadCount = { _ in }
gliaEnv.coreSDKConfigurator.configureWithConfiguration = { _, completion in
completion(.success(()))
}
gliaEnv.coreSDKConfigurator.configureWithInteractor = { _ in }

let sdk = Glia(environment: gliaEnv)
try sdk.configure(
with: .mock(),
theme: .mock()
) { _ in }

XCTAssertTrue(sdk.pendingInteraction.hasPendingInteraction)
XCTAssertTrue(try XCTUnwrap(sdk.pendingInteraction).hasPendingInteraction)
}

func test_hasPendingInteractionIfUnreadMessagesExist() {
func test_hasPendingInteractionIfUnreadMessagesExist() throws {
var gliaEnv = Glia.Environment.failing
var logger = CoreSdkClient.Logger.failing
let uuidGen = UUID.incrementing
logger.configureLocalLogLevelClosure = { _ in }
logger.configureRemoteLogLevelClosure = { _ in }
logger.infoClosure = { _, _, _, _ in }
logger.prefixedClosure = { _ in logger }
gliaEnv.coreSdk.createLogger = { _ in logger }
gliaEnv.conditionalCompilation.isDebug = { true }
gliaEnv.coreSdk.subscribeForUnreadSCMessageCount = { callback in
Expand All @@ -789,28 +801,50 @@ final class GliaTests: XCTestCase {
}
gliaEnv.coreSdk.unsubscribeFromPendingSecureConversationStatus = { _ in }
gliaEnv.coreSdk.unsubscribeFromUnreadCount = { _ in }
gliaEnv.coreSDKConfigurator.configureWithConfiguration = { _, completion in
completion(.success(()))
}
gliaEnv.coreSDKConfigurator.configureWithInteractor = { _ in }

let sdk = Glia(environment: gliaEnv)

XCTAssertTrue(sdk.pendingInteraction.hasPendingInteraction)
try sdk.configure(
with: .mock(),
theme: .mock()
) { _ in }

XCTAssertTrue(try XCTUnwrap(sdk.pendingInteraction).hasPendingInteraction)
}

func test_hasPendingInteractionIfNoUnreadMessageAndPendingSecureConversationExist() {
func test_hasPendingInteractionIfNoUnreadMessageAndPendingSecureConversationExist() throws {
var uuidGen = UUID.incrementing
var gliaEnv = Glia.Environment.failing
var logger = CoreSdkClient.Logger.failing
logger.configureLocalLogLevelClosure = { _ in }
logger.configureRemoteLogLevelClosure = { _ in }
logger.configureRemoteLogLevelClosure = { _ in }
logger.infoClosure = { _, _, _, _ in }
logger.prefixedClosure = { _ in logger }
gliaEnv.coreSdk.createLogger = { _ in logger }
gliaEnv.coreSdk.pendingSecureConversationStatus = { $0(.success(false)) }
gliaEnv.coreSdk.getSecureUnreadMessageCount = { $0(.success(0)) }
gliaEnv.conditionalCompilation.isDebug = { true }
gliaEnv.coreSdk.subscribeForUnreadSCMessageCount = { _ in nil }
gliaEnv.coreSdk.observePendingSecureConversationStatus = { _ in nil }
gliaEnv.coreSdk.subscribeForUnreadSCMessageCount = { _ in uuidGen().uuidString }
gliaEnv.coreSdk.observePendingSecureConversationStatus = { _ in uuidGen().uuidString }
gliaEnv.coreSdk.unsubscribeFromPendingSecureConversationStatus = { _ in }
gliaEnv.coreSdk.unsubscribeFromUnreadCount = { _ in }
gliaEnv.coreSDKConfigurator.configureWithConfiguration = { _, completion in
completion(.success(()))
}
gliaEnv.coreSDKConfigurator.configureWithInteractor = { _ in }

let sdk = Glia(environment: gliaEnv)

XCTAssertFalse(sdk.pendingInteraction.hasPendingInteraction)
try sdk.configure(
with: .mock(),
theme: .mock()
) { _ in }

XCTAssertFalse(try XCTUnwrap(sdk.pendingInteraction).hasPendingInteraction)
}
}

0 comments on commit 76c87b1

Please sign in to comment.