Skip to content

Commit

Permalink
Merge pull request #2302 from minvws/bugfix/strippenrefresh_error_text
Browse files Browse the repository at this point in the history
  • Loading branch information
iandundas authored Mar 3, 2023
2 parents c5972d3 + 9c6d8da commit 83e1191
Show file tree
Hide file tree
Showing 9 changed files with 46 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ extension HolderDashboardViewModelTests {
sut = vendSut(dashboardRegionToggleValue: .domestic, activeDisclosurePolicies: [.policy3G])

// Act
sendUpdate?((RemoteConfiguration.default, Data(), URLResponse()))
sendUpdate?((RemoteConfiguration.default, Data(), URLResponse(), "hash"))

// Assert

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public class DisclosurePolicyManager: DisclosurePolicyManaging {

private func configureRemoteConfigManager() {

remoteConfigManagerObserverToken = remoteConfigManager.observatoryForUpdates.append { [weak self] _, _, _ in
remoteConfigManagerObserverToken = remoteConfigManager.observatoryForUpdates.append { [weak self] _, _, _, _ in
self?.detectPolicyChange()
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ public protocol RemoteConfigManaging: AnyObject {

/// The remote configuration manager
public class RemoteConfigManager: RemoteConfigManaging {
public typealias ConfigNotification = (RemoteConfiguration, Data, URLResponse)
/// String is the config hash:
public typealias ConfigNotification = (RemoteConfiguration, Data, URLResponse, String)

// MARK: - Vars

Expand Down Expand Up @@ -200,15 +201,15 @@ public class RemoteConfigManager: RemoteConfigManaging {
storedConfiguration = remoteConfiguration

// Some observers want to know whenever the config is reloaded (regardless if data changed since last time):
self.notifyReloadObservers(Result.success((remoteConfiguration: remoteConfiguration, data: data, response: urlResponse)))
self.notifyReloadObservers(Result.success((remoteConfiguration: remoteConfiguration, data: data, response: urlResponse, hash: newHash ?? "")))

// Is the newly fetched config hash the same as the existing one?
// Use the hash, as not all of the config values are mapping in the remoteconfig object.
if hashesMatch {
completion(.success((false, remoteConfiguration)))
} else {
// Inform the observers that only wish to know when config has changed:
notifyUpdateObservers((remoteConfiguration: remoteConfiguration, data: data, response: urlResponse))
notifyUpdateObservers((remoteConfiguration: remoteConfiguration, data: data, response: urlResponse, hash: newHash ?? ""))
completion(.success((true, remoteConfiguration)))
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public final class VerificationPolicyEnabler: VerificationPolicyEnableable {

private func configureRemoteConfigManager() {

remoteConfigManagerObserverToken = remoteConfigManager.observatoryForUpdates.append { [weak self] remoteConfiguration, _, _ in
remoteConfigManagerObserverToken = remoteConfigManager.observatoryForUpdates.append { [weak self] remoteConfiguration, _, _, _ in
guard let policies = remoteConfiguration.verificationPolicies else {
// No feature flag available, enable default policy
self?.enable(verificationPolicies: [])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ class RemoteConfigManagerTests: XCTestCase {
_ = sut.observatoryForReloads.append { result in
reloadObserverReceivedConfiguration = result.successValue?.0
}
_ = sut.observatoryForUpdates.append { remoteConfiguration, _, _ in
_ = sut.observatoryForUpdates.append { remoteConfiguration, _, _, _ in
updateObserverReceivedConfiguration = remoteConfiguration
}

Expand Down Expand Up @@ -297,7 +297,7 @@ class RemoteConfigManagerTests: XCTestCase {
_ = sut.observatoryForReloads.append { result in
reloadObserverReceivedConfiguration = result.successValue?.0
}
_ = sut.observatoryForUpdates.append { remoteConfiguration, _, _ in
_ = sut.observatoryForUpdates.append { remoteConfiguration, _, _, _ in
updateObserverReceivedConfiguration = remoteConfiguration
}

Expand Down Expand Up @@ -389,7 +389,7 @@ class RemoteConfigManagerTests: XCTestCase {
_ = sut.observatoryForReloads.append { result in
reloadObserverReceivedConfiguration = result.successValue?.0
}
_ = sut.observatoryForUpdates.append { remoteConfiguration, _, _ in
_ = sut.observatoryForUpdates.append { remoteConfiguration, _, _, _ in
updateObserverReceivedConfiguration = remoteConfiguration
}

Expand Down Expand Up @@ -427,7 +427,7 @@ class RemoteConfigManagerTests: XCTestCase {
_ = sut.observatoryForReloads.append { result in
reloadObserverReceivedConfiguration = result.successValue?.0
}
_ = sut.observatoryForUpdates.append { remoteConfiguration, _, _ in
_ = sut.observatoryForUpdates.append { remoteConfiguration, _, _, _ in
updateObserverReceivedConfiguration = remoteConfiguration
}

Expand Down
4 changes: 2 additions & 2 deletions Sources/CTR/Interface/AppCoordinator/LaunchStateManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ final class LaunchStateManager: LaunchStateManaging {
// Attach behaviours that we want the RemoteConfigManager to perform
// each time it refreshes the config in future:

remoteConfigManagerUpdateObserverToken = Current.remoteConfigManager.observatoryForUpdates.append { _, rawData, _ in
remoteConfigManagerUpdateObserverToken = Current.remoteConfigManager.observatoryForUpdates.append { _, rawData, _, _ in

// Update the remote config for the crypto library
Current.cryptoLibUtility.store(rawData, for: .remoteConfiguration)
Expand Down Expand Up @@ -169,7 +169,7 @@ final class LaunchStateManager: LaunchStateManaging {
// We are within the TTL. Nothing to do.
break
}
case .success(let (_, _, urlResponse)):
case .success(let (_, _, urlResponse, _)):
// Mark remote config loaded
Current.cryptoLibUtility.checkFile(.remoteConfiguration)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,17 +93,23 @@ final class HolderDashboardViewModel: HolderDashboardViewModelType {
/// that allows us to use a `didSet{}` to
/// get a callback if any of them are mutated.
struct State: Equatable {
enum StrippenRefresherFailMissingCredentialsError: Error { // swiftlint:disable:this type_name
case noInternet
case otherFailureFirstOccurence, otherFailureSubsequentOccurence
}

var qrCards: [QRCard]
var expiredGreenCards: [ExpiredQR]
var blockedEventItems: [RemovedEventItem]
var mismatchedIdentityItems: [RemovedEventItem]
var isRefreshingStrippen: Bool
var lastKnownConfigHash: String?

// Related to strippen refreshing.
// When there's an error with the refreshing process,
// we show an error message on each QR card that lacks credentials.
// This does not discriminate between domestic/EU.
var errorForQRCardsMissingCredentials: String?
var errorForQRCardsMissingCredentials: StrippenRefresherFailMissingCredentialsError?

var deviceHasClockDeviation: Bool = false

Expand Down Expand Up @@ -211,7 +217,6 @@ final class HolderDashboardViewModel: HolderDashboardViewModelType {
// Observation tokens:
private var remoteConfigUpdateObserverToken: Observatory.ObserverToken?
private var clockDeviationObserverToken: Observatory.ObserverToken?
private var remoteConfigUpdatesConfigurationWarningToken: Observatory.ObserverToken?
private var disclosurePolicyUpdateObserverToken: Observatory.ObserverToken?
private var configurationAlmostOutOfDateObserverToken: Observatory.ObserverToken?

Expand Down Expand Up @@ -271,7 +276,6 @@ final class HolderDashboardViewModel: HolderDashboardViewModelType {
setupFuzzyMatchingRemovedEventsDatasource()
setupStrippenRefresher()
setupNotificationListeners()
setupConfigNotificationManager()
setupRecommendedVersion()
recalculateActiveDisclosurePolicyMode()
recalculateDisclosureBannerState()
Expand All @@ -289,8 +293,10 @@ final class HolderDashboardViewModel: HolderDashboardViewModelType {
}

// If the config ever changes, reload dependencies:
remoteConfigUpdateObserverToken = Current.remoteConfigManager.observatoryForUpdates.append { [weak self] _, _, _ in
remoteConfigUpdateObserverToken = Current.remoteConfigManager.observatoryForUpdates.append { [weak self] _, _, _, hash in
self?.strippenRefresher.load()
self?.setupRecommendedVersion() // Config changed, check recommended version.
self?.state.lastKnownConfigHash = hash
}

disclosurePolicyUpdateObserverToken = Current.disclosurePolicyManager.observatory.append { [weak self] in
Expand All @@ -317,7 +323,6 @@ final class HolderDashboardViewModel: HolderDashboardViewModelType {
clockDeviationObserverToken.map(Current.clockDeviationManager.observatory.remove)
disclosurePolicyUpdateObserverToken.map(Current.disclosurePolicyManager.observatory.remove)
remoteConfigUpdateObserverToken.map(Current.remoteConfigManager.observatoryForUpdates.remove)
remoteConfigUpdatesConfigurationWarningToken.map(Current.remoteConfigManager.observatoryForReloads.remove)
}

// MARK: - Setup
Expand Down Expand Up @@ -370,14 +375,6 @@ final class HolderDashboardViewModel: HolderDashboardViewModelType {
strippenRefresher.load()
}

func setupConfigNotificationManager() {

remoteConfigUpdatesConfigurationWarningToken = Current.remoteConfigManager.observatoryForReloads.append { [weak self] result in
guard let self, case .success = result else { return }
self.setupRecommendedVersion()
}
}

// MARK: - View Lifecycle callbacks:

func viewWillAppear() {
Expand Down Expand Up @@ -461,7 +458,7 @@ final class HolderDashboardViewModel: HolderDashboardViewModelType {

case (.noInternet, .expired, true):
logDebug("StrippenRefresh: Need refreshing now, but no internet. Showing in UI.")
state.errorForQRCardsMissingCredentials = L.holderDashboardStrippenExpiredErrorfooterNointernet()
state.errorForQRCardsMissingCredentials = .noInternet

case (.noInternet, .expiring, true):
// Do nothing
Expand All @@ -478,8 +475,8 @@ final class HolderDashboardViewModel: HolderDashboardViewModelType {
checkForMismatchedIdentityError(error: error)

state.errorForQRCardsMissingCredentials = refresherState.errorOccurenceCount > 1
? L.holderDashboardStrippenExpiredErrorfooterServerHelpdesk(Current.contactInformationProvider.phoneNumberLink)
: L.holderDashboardStrippenExpiredErrorfooterServerTryagain(AppAction.tryAgain)
? .otherFailureSubsequentOccurence
: .otherFailureFirstOccurence

case let (.failed(error), .expiring, _):
// In this case we just swallow the server errors.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,15 @@ extension HolderDashboardViewModel {
let firstOrigin = firstGreenCard.origins.first
else { return .greatestFiniteMagnitude }

return firstOrigin.customSortIndex
// DCCs and CTBs should be grouped when the GreenCards are sorted:
let regionModifier: Double = {
switch self.region {
case .europeanUnion: return 1
case .netherlands: return 0
}
}()

return firstOrigin.customSortIndex + regionModifier
}

var origins: [GreenCard.Origin] {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
*
* SPDX-License-Identifier: EUPL-1.2
*/
// swiftlint:disable file_length

import Foundation
import Shared
Expand Down Expand Up @@ -572,7 +573,16 @@ extension HolderDashboardViewModel.QRCard {
/// Returns `HolderDashboardViewController.Card.Error`, if appropriate, which configures the display of an error on the QRCardView.
private func qrCardError(state: HolderDashboardViewModel.State, actionHandler: HolderDashboardCardUserActionHandling) -> HolderDashboardViewController.Card.Error? {
guard let error = state.errorForQRCardsMissingCredentials, shouldShowErrorBeneathCard else { return nil }
return HolderDashboardViewController.Card.Error(message: error, didTapURL: { [weak actionHandler] url in

let errorMessage: String = {
switch error {
case .noInternet: return L.holderDashboardStrippenExpiredErrorfooterNointernet()
case .otherFailureFirstOccurence: return L.holderDashboardStrippenExpiredErrorfooterServerTryagain(AppAction.tryAgain)
case .otherFailureSubsequentOccurence: return L.holderDashboardStrippenExpiredErrorfooterServerHelpdesk(Current.contactInformationProvider.phoneNumberLink)
}
}()

return HolderDashboardViewController.Card.Error(message: errorMessage, didTapURL: { [weak actionHandler] url in
if url.absoluteString == AppAction.tryAgain {
actionHandler?.didTapRetryLoadQRCards()
} else {
Expand Down

0 comments on commit 83e1191

Please sign in to comment.