Skip to content

Commit

Permalink
Handle engagement restart after visitor authentication
Browse files Browse the repository at this point in the history
MOB-3095
  • Loading branch information
yurii-glia committed Mar 21, 2024
1 parent 66c5528 commit 09a91b2
Show file tree
Hide file tree
Showing 10 changed files with 149 additions and 14 deletions.
4 changes: 4 additions & 0 deletions GliaWidgets.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,7 @@
755D187929A6A6F80009F5E8 /* WelcomeStyle.FilePickerButtonStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 755D187829A6A6F80009F5E8 /* WelcomeStyle.FilePickerButtonStyle.swift */; };
755D187B29A6A7180009F5E8 /* WelcomeStyle.TitleImageStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 755D187A29A6A7180009F5E8 /* WelcomeStyle.TitleImageStyle.swift */; };
755D187F29A6B1B90009F5E8 /* Glia+StartEngagement.swift in Sources */ = {isa = PBXBuildFile; fileRef = 755D187E29A6B1B90009F5E8 /* Glia+StartEngagement.swift */; };
75617DFF2B9F899B002E403D /* GliaTests+DirectId.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75617DFE2B9F899B002E403D /* GliaTests+DirectId.swift */; };
756979C52A1E5E3C002ED254 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 756979C42A1E5E3C002ED254 /* Assets.xcassets */; };
756B8B1C2996B116001D2BB2 /* ChatCoordinator.Environment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 756B8B1B2996B116001D2BB2 /* ChatCoordinator.Environment.swift */; };
756B8B1E2996BAEA001D2BB2 /* HeaderButton.Props.swift in Sources */ = {isa = PBXBuildFile; fileRef = 756B8B1D2996BAEA001D2BB2 /* HeaderButton.Props.swift */; };
Expand Down Expand Up @@ -1193,6 +1194,7 @@
755D187829A6A6F80009F5E8 /* WelcomeStyle.FilePickerButtonStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeStyle.FilePickerButtonStyle.swift; sourceTree = "<group>"; };
755D187A29A6A7180009F5E8 /* WelcomeStyle.TitleImageStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeStyle.TitleImageStyle.swift; sourceTree = "<group>"; };
755D187E29A6B1B90009F5E8 /* Glia+StartEngagement.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Glia+StartEngagement.swift"; sourceTree = "<group>"; };
75617DFE2B9F899B002E403D /* GliaTests+DirectId.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "GliaTests+DirectId.swift"; sourceTree = "<group>"; };
756979C42A1E5E3C002ED254 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
756B8B1B2996B116001D2BB2 /* ChatCoordinator.Environment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatCoordinator.Environment.swift; sourceTree = "<group>"; };
756B8B1D2996BAEA001D2BB2 /* HeaderButton.Props.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeaderButton.Props.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -3801,6 +3803,7 @@
children = (
8491AF612AA20F8F00CC3E72 /* Mocks */,
7512A5A627C3926500319DF1 /* GliaTests.swift */,
75617DFE2B9F899B002E403D /* GliaTests+DirectId.swift */,
846A5C4429F6BEFA0049B29F /* GliaTests+StartEngagement.swift */,
);
path = Glia;
Expand Down Expand Up @@ -5967,6 +5970,7 @@
AF9DB23128905A1D00A0C442 /* ViewFactory.Environment.Failing.swift in Sources */,
9A3E1DA027BA7B9F005634EB /* FileSystemStorageTests.swift in Sources */,
EB9ADB5A2829089F00FAE8A4 /* ChatItem+Equatable.swift in Sources */,
75617DFF2B9F899B002E403D /* GliaTests+DirectId.swift in Sources */,
EB7A150A286D98270035AC62 /* FileUploaderTests.swift in Sources */,
EB27E71D27FEBB620090B895 /* CallViewModelTests.swift in Sources */,
846A5C3E29D1C7B00049B29F /* CallVisualizerTests.swift in Sources */,
Expand Down
7 changes: 6 additions & 1 deletion GliaWidgets/Public/Glia/Glia+StartEngagement.swift
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,12 @@ extension Glia {
log: loggerPhase.logger,
snackBar: environment.snackBar,
operatorRequestHandlerService: operatorRequestHandlerService,
maximumUploads: { self.maximumUploads }
maximumUploads: { self.maximumUploads },
reloadAllChild: { coordinators in
coordinators.compactMap { $0 as? ChatCoordinator }
.first?
.reloadChatTranscript()
}
)
)
rootCoordinator?.delegate = { [weak self] event in
Expand Down
30 changes: 21 additions & 9 deletions GliaWidgets/Public/Glia/Glia.OpaqueAuthentication.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ extension Glia.Authentication {
public enum Behavior {
/// Restrict authentication and deauthentication during ongoing engagement.
case forbiddenDuringEngagement

case allowedDuringEngagement
}
}

Expand All @@ -24,6 +26,8 @@ extension Glia.Authentication.Behavior {
switch behavior {
case .forbiddenDuringEngagement:
self = .forbiddenDuringEngagement
case .allowedDuringEngagement:
self = .allowedDuringEngagement
@unknown default:
self = .forbiddenDuringEngagement
}
Expand All @@ -33,6 +37,8 @@ extension Glia.Authentication.Behavior {
switch self {
case .forbiddenDuringEngagement:
return .forbiddenDuringEngagement
case .allowedDuringEngagement:
return .allowedDuringEngagement
}
}
}
Expand All @@ -59,12 +65,18 @@ extension Glia {
public func authentication(with behavior: Glia.Authentication.Behavior) throws -> Authentication {
let auth = try environment.coreSdk.authentication(behavior.toCoreSdk())

// Reset navigation and UI back to initial state,
// effectively removing bubble view (if there was one).
let cleanup = { [weak self] in
self?.rootCoordinator?.popCoordinator()
self?.rootCoordinator?.end()
self?.rootCoordinator = nil
let completion = { [weak self] in
self?.environment.gcd.mainQueue.asyncAfterDeadline(.now() + .seconds(2)) { [weak self] in
if self?.interactor?.currentEngagement?.restartedFromEngagementId != nil {
self?.rootCoordinator?.reload()
} else {
// Reset navigation and UI back to initial state,
// effectively removing bubble view (if there was one).
self?.rootCoordinator?.popCoordinator()
self?.rootCoordinator?.end()
self?.rootCoordinator = nil
}
}
}

return .init(
Expand All @@ -78,8 +90,8 @@ extension Glia {
) { result in
switch result {
case .success:
// Cleanup navigation and views.
cleanup()
// Handle authentication
completion()

case .failure:
break
Expand All @@ -94,7 +106,7 @@ extension Glia {
switch result {
case .success:
// Cleanup navigation and views.
cleanup()
completion()
case .failure:
break
}
Expand Down
4 changes: 4 additions & 0 deletions GliaWidgets/Sources/Coordinators/Chat/ChatCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ class ChatCoordinator: SubFlowCoordinator, FlowCoordinator {
return viewController
}

func reloadChatTranscript() {
controller?.viewModel.event(.viewDidLoad)
}

private func makeChatViewController() -> ChatViewController {
// We need to defer passing controller to transcript model,
// because model will use it later, however controller
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ extension EngagementCoordinator.Environment {
log: .mock,
snackBar: .mock,
operatorRequestHandlerService: .mock(),
maximumUploads: { 2 }
maximumUploads: { 2 },
reloadAllChild: { _ in }
)
}
#endif
Original file line number Diff line number Diff line change
Expand Up @@ -41,5 +41,6 @@ extension EngagementCoordinator {
var snackBar: SnackBar
var operatorRequestHandlerService: OperatorRequestHandlerService
var maximumUploads: () -> Int
var reloadAllChild: ([any FlowCoordinator]) -> Void
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,10 @@ class EngagementCoordinator: SubFlowCoordinator, FlowCoordinator {
}
// swiftlint:enable function_body_length

func reload() {
environment.reloadAllChild(coordinators)
}

deinit {
print("\(Self.self) is deallocated.")
}
Expand Down
8 changes: 5 additions & 3 deletions GliaWidgets/Sources/CoreSDKClient/CoreSDKClient.Mock.swift
Original file line number Diff line number Diff line change
Expand Up @@ -483,13 +483,15 @@ extension CoreSdkClient.Engagement {
id: String = "",
engagedOperator: Operator? = nil,
source: EngagementSource = .coreEngagement,
fetchSurvey: @escaping FetchSurvey = { _, _ in })
-> CoreSdkClient.Engagement {
fetchSurvey: @escaping FetchSurvey = { _, _ in },
restartedFromEngagementId: String? = nil
) -> CoreSdkClient.Engagement {
.init(
id: id,
engagedOperator: engagedOperator,
source: source,
fetchSurvey: fetchSurvey
fetchSurvey: fetchSurvey,
restartedFromEngagementId: restartedFromEngagementId
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,9 @@ extension EngagementCoordinator.Environment {
maximumUploads: {
fail("\(Self.self).maximumUploads")
return 2
},
reloadAllChild: { _ in
fail("\(Self.self).reloadAllChild")
}
)
}
99 changes: 99 additions & 0 deletions GliaWidgetsTests/Sources/Glia/GliaTests+DirectId.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import XCTest

@testable import GliaWidgets

final class GliaTestsDirectId: XCTestCase {
/// If ongoing engagement exists, it should be restarted after autentication.
/// To verify which should ongoing engagement restored SDK relyies on
/// `restartedEngagementId` property in `Engagement` instance.
func testAuthentication__shouldRestartEngagementAndFetchHistory() throws {
enum Call { case reloadAllChild }

var calls = [Call]()
var authenticationResult: Result<Void, Glia.Authentication.Error>?

var env = Glia.Environment.failing
env.coreSdk.createLogger = { _ in .mock }
env.gcd.mainQueue.asyncIfNeeded = { $0() }
env.gcd.mainQueue.asyncAfterDeadline = { $1() }
env.coreSdk.authentication = { _ in
CoreSdkClient.Authentication(authenticateWithIdToken: { $2(.success(())) })
}
env.coreSDKConfigurator.configureWithInteractor = { _ in }
env.coreSDKConfigurator.configureWithConfiguration = { $1(.success(())) }
env.createRootCoordinator = { _, _, _, _, _, _, _ in
var coordinatorEnv = EngagementCoordinator.Environment.mock
coordinatorEnv.reloadAllChild = { _ in calls.append(.reloadAllChild) }
return .mock(environment: coordinatorEnv)
}
env.coreSdk.localeProvider = .mock
env.conditionalCompilation = .mock

// Use case
let sdk = Glia(environment: env)
try sdk.configure(with: .mock(), theme: .mock()) { _ in }
try sdk.startEngagement(engagementKind: .chat, in: ["queue-id"])

sdk.interactor?.currentEngagement = .mock(restartedFromEngagementId: "restarted-id")
XCTAssertNotNil(sdk.rootCoordinator)

let authentication = try sdk.authentication(with: .allowedDuringEngagement)
authentication.authenticate(with: "", accessToken: nil) { result in
authenticationResult = result
}

XCTAssertEqual(authenticationResult?.map { true }, .some(.success(true)))
XCTAssertEqual(calls, [.reloadAllChild])
XCTAssertNotNil(sdk.rootCoordinator)
}

/// If after authentication a visitor has no ongoing engagement or engagement without
/// `restartedEngagementId` property in `Engagement` instance it should not be restored
/// automatically.
func testAuthentication__shouldNotSstartOngoingEngagementAfterAuthentication() throws {
enum Call { case reloadAllChild }

var calls = [Call]()
var authenticationResult: Result<Void, Glia.Authentication.Error>?

var env = Glia.Environment.failing
env.coreSdk.createLogger = { _ in .mock }
env.gcd.mainQueue.asyncIfNeeded = { $0() }
env.gcd.mainQueue.asyncAfterDeadline = { $1() }
env.coreSdk.authentication = { _ in
CoreSdkClient.Authentication(authenticateWithIdToken: { $2(.success(())) })
}
env.coreSDKConfigurator.configureWithInteractor = { _ in }
env.coreSDKConfigurator.configureWithConfiguration = { $1(.success(())) }
env.createRootCoordinator = { _, _, _, _, _, _, _ in
var coordinatorEnv = EngagementCoordinator.Environment.mock
coordinatorEnv.reloadAllChild = { _ in calls.append(.reloadAllChild) }
return .mock(environment: coordinatorEnv)
}
env.coreSdk.localeProvider = .mock
env.conditionalCompilation = .mock

// Use case
let sdk = Glia(environment: env)
try sdk.configure(with: .mock(), theme: .mock()) { _ in }
try sdk.startEngagement(engagementKind: .chat, in: ["queue-id"])

sdk.interactor?.currentEngagement = .mock()
XCTAssertNotNil(sdk.rootCoordinator)

let authentication = try sdk.authentication(with: .allowedDuringEngagement)
authentication.authenticate(with: "", accessToken: nil) { result in
authenticationResult = result
}

XCTAssertEqual(authenticationResult?.map { true }, .some(.success(true)))
XCTAssertEqual(calls, [])
XCTAssertNil(sdk.rootCoordinator)
}
}

extension Glia.Authentication.Error: Equatable {
public static func == (lhs: Glia.Authentication.Error, rhs: Glia.Authentication.Error) -> Bool {
lhs.reason == rhs.reason
}
}

0 comments on commit 09a91b2

Please sign in to comment.