Skip to content

Commit fd0462d

Browse files
committed
Update POSSystemStatusService to also fetch inactive plugins and upsert all plugins to storage so that later use cases have the latest value.
1 parent d6116a7 commit fd0462d

File tree

7 files changed

+100
-11
lines changed

7 files changed

+100
-11
lines changed

Modules/Sources/Storage/Protocols/StorageManagerType.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,4 +57,17 @@ public extension StorageManagerType {
5757
func reset() {
5858
reset(onCompletion: nil)
5959
}
60+
61+
/// Async/await version of `performAndSave`.
62+
///
63+
/// - Parameters:
64+
/// - operation: A closure which uses the given `StorageType` to make data changes in background.
65+
/// - queue: A queue on which to execute the completion closure.
66+
func performAndSaveAsync(_ operation: @escaping (StorageType) -> Void, on queue: DispatchQueue = .main) async {
67+
await withCheckedContinuation { continuation in
68+
performAndSave(operation, completion: {
69+
continuation.resume()
70+
}, on: queue)
71+
}
72+
}
6073
}

Modules/Sources/Yosemite/PointOfSale/Eligibility/POSSystemStatusService.swift

Lines changed: 53 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import Foundation
22
import Networking
3+
import Storage
34

45
public protocol POSSystemStatusServiceProtocol {
56
/// Loads WooCommerce plugin and POS feature switch value remotely for eligibility checks.
@@ -23,27 +24,36 @@ public struct POSPluginAndFeatureInfo {
2324
/// Service for fetching POS-related system status information.
2425
public final class POSSystemStatusService: POSSystemStatusServiceProtocol {
2526
private let remote: SystemStatusRemote
27+
private let storageManager: StorageManagerType
2628

27-
public init(credentials: Credentials?) {
29+
public init(credentials: Credentials?, storageManager: StorageManagerType) {
2830
let network = AlamofireNetwork(credentials: credentials)
29-
remote = SystemStatusRemote(network: network)
31+
self.remote = SystemStatusRemote(network: network)
32+
self.storageManager = storageManager
3033
}
3134

3235
/// Test-friendly initializer that accepts a network implementation.
33-
init(network: Network) {
34-
remote = SystemStatusRemote(network: network)
36+
init(network: Network, storageManager: StorageManagerType) {
37+
self.remote = SystemStatusRemote(network: network)
38+
self.storageManager = storageManager
3539
}
3640

41+
@MainActor
3742
public func loadWooCommercePluginAndPOSFeatureSwitch(siteID: Int64) async throws -> POSPluginAndFeatureInfo {
3843
let mapper = SingleItemMapper<POSPluginEligibilitySystemStatus>(siteID: siteID)
3944
let systemStatus: POSPluginEligibilitySystemStatus = try await remote.loadSystemStatus(
4045
for: siteID,
41-
fields: [.activePlugins, .settings],
46+
fields: [.activePlugins, .inactivePlugins, .settings],
4247
mapper: mapper
4348
)
4449

45-
// Finds WooCommerce plugin from active plugins response.
46-
guard let wcPlugin = systemStatus.activePlugins.first(where: { $0.plugin == Constants.wcPluginPath }) else {
50+
// Upserts all plugins in storage.
51+
await storageManager.performAndSaveAsync({ [weak self] storage in
52+
self?.upsertSystemPlugins(siteID: siteID, systemStatus: systemStatus, in: storage)
53+
})
54+
55+
// Loads WooCommerce plugin from storage.
56+
guard let wcPlugin = storageManager.viewStorage.loadSystemPlugin(siteID: siteID, path: Constants.wcPluginPath)?.toReadOnly() else {
4757
return POSPluginAndFeatureInfo(wcPlugin: nil, featureValue: nil)
4858
}
4959

@@ -57,16 +67,52 @@ private extension POSSystemStatusService {
5767
enum Constants {
5868
static let wcPluginPath = "woocommerce/woocommerce.php"
5969
}
70+
71+
/// Updates or inserts system plugins in storage.
72+
func upsertSystemPlugins(siteID: Int64, systemStatus: POSPluginEligibilitySystemStatus, in storage: StorageType) {
73+
// Active and inactive plugins share identical structure, but are stored in separate parts of the remote response
74+
// (and without an active attribute in the response). So we apply the correct value for active (or not)
75+
let readonlySystemPlugins: [SystemPlugin] = {
76+
let activePlugins = systemStatus.activePlugins.map {
77+
$0.copy(active: true)
78+
}
79+
80+
let inactivePlugins = systemStatus.inactivePlugins.map {
81+
$0.copy(active: false)
82+
}
83+
84+
return activePlugins + inactivePlugins
85+
}()
86+
87+
let storedPlugins = storage.loadSystemPlugins(siteID: siteID, matching: readonlySystemPlugins.map { $0.name })
88+
readonlySystemPlugins.forEach { readonlySystemPlugin in
89+
// Loads or creates new StorageSystemPlugin matching the readonly one.
90+
let storageSystemPlugin: StorageSystemPlugin = {
91+
if let systemPlugin = storedPlugins.first(where: { $0.name == readonlySystemPlugin.name }) {
92+
return systemPlugin
93+
}
94+
return storage.insertNewObject(ofType: StorageSystemPlugin.self)
95+
}()
96+
97+
storageSystemPlugin.update(with: readonlySystemPlugin)
98+
}
99+
100+
// Removes stale system plugins.
101+
let currentSystemPlugins = readonlySystemPlugins.map(\.name)
102+
storage.deleteStaleSystemPlugins(siteID: siteID, currentSystemPlugins: currentSystemPlugins)
103+
}
60104
}
61105

62106
// MARK: - Network Response Structs
63107

64108
private struct POSPluginEligibilitySystemStatus: Decodable {
65109
let activePlugins: [SystemPlugin]
110+
let inactivePlugins: [SystemPlugin]
66111
let settings: POSEligibilitySystemStatusSettings
67112

68113
enum CodingKeys: String, CodingKey {
69114
case activePlugins = "active_plugins"
115+
case inactivePlugins = "inactive_plugins"
70116
case settings
71117
}
72118
}

Modules/Tests/NetworkingTests/Responses/system-status-wc-plugin-and-pos-feature-disabled.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
{
22
"data": {
3+
"inactive_plugins": [],
34
"active_plugins":[
45
{
56
"plugin": "woocommerce/woocommerce.php",

Modules/Tests/NetworkingTests/Responses/system-status-wc-plugin-and-pos-feature-enabled.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
{
22
"data": {
3+
"inactive_plugins": [],
34
"active_plugins":[
45
{
56
"plugin": "woocommerce/woocommerce.php",

Modules/Tests/NetworkingTests/Responses/system-status-wc-plugin-missing.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
{
22
"data": {
3+
"inactive_plugins": [],
34
"active_plugins":[
45
],
56
"settings": {

Modules/Tests/YosemiteTests/PointOfSale/POSSystemStatusServiceTests.swift

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,24 @@ import Foundation
22
import Testing
33
import TestKit
44
@testable import Networking
5+
@testable import Storage
56
@testable import Yosemite
67

78
@MainActor
89
struct POSSystemStatusServiceTests {
910
private let network = MockNetwork()
11+
private let storageManager = MockStorageManager()
1012
private let sampleSiteID: Int64 = 134
1113
private let sut: POSSystemStatusService
1214

1315
init() async throws {
1416
network.removeAllSimulatedResponses()
15-
sut = POSSystemStatusService(network: network)
17+
sut = POSSystemStatusService(network: network, storageManager: storageManager)
1618
}
1719

1820
// MARK: - loadWooCommercePluginAndPOSFeatureSwitch Tests
1921

20-
@Test func loadWooCommercePluginAndPOSFeatureSwitch_returns_plugin_and_nil_feature_when_settings_response_does_not_include_enabled_featuers() async throws {
22+
@Test func loadWooCommercePluginAndPOSFeatureSwitch_returns_plugin_and_nil_feature_when_settings_response_does_not_include_enabled_features() async throws {
2123
// Given
2224
network.simulateResponse(requestUrlSuffix: "system_status", filename: "systemStatus")
2325

@@ -37,6 +39,10 @@ struct POSSystemStatusServiceTests {
3739
// Given
3840
network.simulateResponse(requestUrlSuffix: "system_status", filename: "system-status-wc-plugin-and-pos-feature-enabled")
3941

42+
// Inserts WooCommerce plugin into storage with an older version and inactive.
43+
let storageWCPlugin = createWCPlugin(version: "9.5.2", active: false)
44+
storageManager.insertSampleSystemPlugin(readOnlySystemPlugin: storageWCPlugin)
45+
4046
// When
4147
let result = try await sut.loadWooCommercePluginAndPOSFeatureSwitch(siteID: sampleSiteID)
4248

@@ -53,6 +59,10 @@ struct POSSystemStatusServiceTests {
5359
// Given
5460
network.simulateResponse(requestUrlSuffix: "system_status", filename: "system-status-wc-plugin-and-pos-feature-disabled")
5561

62+
// Inserts WooCommerce plugin into storage with an older version and inactive.
63+
let storageWCPlugin = createWCPlugin(version: "9.5.2", active: false)
64+
storageManager.insertSampleSystemPlugin(readOnlySystemPlugin: storageWCPlugin)
65+
5666
// When
5767
let result = try await sut.loadWooCommercePluginAndPOSFeatureSwitch(siteID: sampleSiteID)
5868

@@ -69,6 +79,10 @@ struct POSSystemStatusServiceTests {
6979
// Given
7080
network.simulateResponse(requestUrlSuffix: "system_status", filename: "system-status-wc-plugin-missing")
7181

82+
// Inserts WooCommerce plugin eligible for POS into storage.
83+
let storageWCPlugin = createWCPlugin(version: "9.9.0", active: true)
84+
storageManager.insertSampleSystemPlugin(readOnlySystemPlugin: storageWCPlugin)
85+
7286
// When
7387
let result = try await sut.loadWooCommercePluginAndPOSFeatureSwitch(siteID: sampleSiteID)
7488

@@ -87,3 +101,15 @@ struct POSSystemStatusServiceTests {
87101
}
88102
}
89103
}
104+
105+
private extension POSSystemStatusServiceTests {
106+
func createWCPlugin(version: String = "5.8.0", active: Bool = true) -> Yosemite.SystemPlugin {
107+
.fake().copy(
108+
siteID: sampleSiteID,
109+
plugin: "woocommerce/woocommerce.php",
110+
version: version,
111+
versionLatest: version,
112+
active: active
113+
)
114+
}
115+
}

WooCommerce/Classes/ViewRelated/Dashboard/Settings/POS/POSTabEligibilityChecker.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,8 @@ final class POSTabEligibilityChecker: POSEntryPointEligibilityCheckerProtocol {
6262
eligibilityService: POSEligibilityServiceProtocol = POSEligibilityService(),
6363
stores: StoresManager = ServiceLocator.stores,
6464
featureFlagService: FeatureFlagService = ServiceLocator.featureFlagService,
65-
systemStatusService: POSSystemStatusServiceProtocol = POSSystemStatusService(credentials: ServiceLocator.stores.sessionManager.defaultCredentials)) {
65+
systemStatusService: POSSystemStatusServiceProtocol = POSSystemStatusService(credentials: ServiceLocator.stores.sessionManager.defaultCredentials,
66+
storageManager: ServiceLocator.storageManager)) {
6667
self.siteID = siteID
6768
self.userInterfaceIdiom = userInterfaceIdiom
6869
self.siteSettings = siteSettings
@@ -208,7 +209,7 @@ private extension POSTabEligibilityChecker {
208209
}
209210

210211
func checkWooCommercePluginEligibility(wcPlugin: SystemPlugin?) -> PluginEligibilityState {
211-
guard let wcPlugin else {
212+
guard let wcPlugin, wcPlugin.active else {
212213
return .ineligible(reason: .wooCommercePluginNotFound)
213214
}
214215

0 commit comments

Comments
 (0)