Skip to content

Commit 0429405

Browse files
authored
Merge pull request #5507 from woocommerce/issue/5365-install-jetpack-yosemite-update
JCP: Update Yosemite layer with new actions for installing plugins
2 parents c664570 + 542d883 commit 0429405

File tree

6 files changed

+187
-1
lines changed

6 files changed

+187
-1
lines changed

Networking/Networking.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -552,6 +552,7 @@
552552
DEC51A95274CDA52009F3DF4 /* SitePluginMapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEC51A94274CDA52009F3DF4 /* SitePluginMapper.swift */; };
553553
DEC51A97274DD962009F3DF4 /* plugin.json in Resources */ = {isa = PBXBuildFile; fileRef = DEC51A96274DD962009F3DF4 /* plugin.json */; };
554554
DEC51A99274DDDC9009F3DF4 /* SitePluginMapperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEC51A98274DDDC9009F3DF4 /* SitePluginMapperTests.swift */; };
555+
DEC51A9B274E3206009F3DF4 /* plugin-inactive.json in Resources */ = {isa = PBXBuildFile; fileRef = DEC51A9A274E3206009F3DF4 /* plugin-inactive.json */; };
555556
E12552C526385B05001CEE70 /* ShippingLabelAddressValidationSuccess.swift in Sources */ = {isa = PBXBuildFile; fileRef = E12552C426385B05001CEE70 /* ShippingLabelAddressValidationSuccess.swift */; };
556557
FE28F6E226840DED004465C7 /* User.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE28F6E126840DED004465C7 /* User.swift */; };
557558
FE28F6E426842848004465C7 /* UserMapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE28F6E326842848004465C7 /* UserMapper.swift */; };
@@ -1145,6 +1146,7 @@
11451146
DEC51A94274CDA52009F3DF4 /* SitePluginMapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SitePluginMapper.swift; sourceTree = "<group>"; };
11461147
DEC51A96274DD962009F3DF4 /* plugin.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = plugin.json; sourceTree = "<group>"; };
11471148
DEC51A98274DDDC9009F3DF4 /* SitePluginMapperTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SitePluginMapperTests.swift; sourceTree = "<group>"; };
1149+
DEC51A9A274E3206009F3DF4 /* plugin-inactive.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "plugin-inactive.json"; sourceTree = "<group>"; };
11481150
E12552C426385B05001CEE70 /* ShippingLabelAddressValidationSuccess.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShippingLabelAddressValidationSuccess.swift; sourceTree = "<group>"; };
11491151
F3F25DC15EC1D7C631169CB5 /* Pods_Networking.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Networking.framework; sourceTree = BUILT_PRODUCTS_DIR; };
11501152
F6CEE1CA2AD376C0C28AE9F6 /* Pods-NetworkingTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NetworkingTests.release.xcconfig"; path = "../Pods/Target Support Files/Pods-NetworkingTests/Pods-NetworkingTests.release.xcconfig"; sourceTree = "<group>"; };
@@ -1678,6 +1680,7 @@
16781680
261CF1B3255AD6B30090D8D3 /* payment-gateway-list.json */,
16791681
261CF2CA255C50010090D8D3 /* payment-gateway-list-half.json */,
16801682
DEC51A96274DD962009F3DF4 /* plugin.json */,
1683+
DEC51A9A274E3206009F3DF4 /* plugin-inactive.json */,
16811684
31D27C8E2602B553002EDB1D /* plugins.json */,
16821685
74749B98224135C4005C4CF2 /* product.json */,
16831686
02AAD53E250092A300BA1E26 /* product-add-or-delete.json */,
@@ -2176,6 +2179,7 @@
21762179
CE20179320E3EFA7005B4C18 /* broken-orders.json in Resources */,
21772180
D8FBFF2722D529F2006E3336 /* order-stats-v4-month.json in Resources */,
21782181
2685C0DE263B5A4200D9EE97 /* add-on-groups.json in Resources */,
2182+
DEC51A9B274E3206009F3DF4 /* plugin-inactive.json in Resources */,
21792183
CCF48B382628AEAE0034EA83 /* shipping-label-account-settings-no-payment-methods.json in Resources */,
21802184
D88D5A43230BC668007B6E01 /* reviews-single.json in Resources */,
21812185
02DD6492248A3EC00082523E /* product-external.json in Resources */,
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
{
2+
"data": {
3+
"plugin": "jetpack/jetpack",
4+
"status": "inactive",
5+
"name": "Jetpack by WordPress.com",
6+
"plugin_uri": "https://jetpack.com",
7+
"author": "Automattic",
8+
"author_uri": "https://jetpack.com",
9+
"description": {
10+
"raw": "Bring the power of the WordPress.com cloud to your self-hosted WordPress.",
11+
"rendered": "Bring the power of the WordPress.com cloud to your self-hosted WordPress. <cite>By <a href=\"https://jetpack.com\">Automattic</a>.</cite>"
12+
},
13+
"version": "9.5",
14+
"network_only": false,
15+
"requires_wp": "5.6",
16+
"requires_php": "5.6",
17+
"textdomain": "jetpack",
18+
"_links": {
19+
"self": [
20+
{
21+
"href": "https://example.com/wp-json/wp/v2/plugins/jetpack/jetpack"
22+
}
23+
]
24+
}
25+
}
26+
}

WooCommerce/WooCommerceTests/ViewRelated/Dashboard/Plugins/SitePluginListViewModelTests.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,8 @@ class PluginListViewModelTests: XCTestCase {
232232
switch action {
233233
case .synchronizeSitePlugins(let siteID, _):
234234
triggeredSiteID = siteID
235+
default:
236+
break
235237
}
236238
}
237239
let viewModel = PluginListViewModel(siteID: sampleSiteID, storesManager: storesManager)
@@ -250,6 +252,8 @@ class PluginListViewModelTests: XCTestCase {
250252
switch action {
251253
case .synchronizeSitePlugins(_, let completion):
252254
completion(.success(()))
255+
default:
256+
break
253257
}
254258
}
255259
let viewModel = PluginListViewModel(siteID: sampleSiteID, storesManager: storesManager)
@@ -272,6 +276,8 @@ class PluginListViewModelTests: XCTestCase {
272276
switch action {
273277
case .synchronizeSitePlugins(_, let completion):
274278
completion(.failure(MockPluginError.mockError))
279+
default:
280+
break
275281
}
276282
}
277283
let viewModel = PluginListViewModel(siteID: sampleSiteID, storesManager: storesManager)

Yosemite/Yosemite/Actions/SitePluginAction.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,13 @@ public enum SitePluginAction: Action {
66

77
/// Synchronize all plugins for a site given its ID
88
case synchronizeSitePlugins(siteID: Int64, onCompletion: (Result<Void, Error>) -> Void)
9+
10+
/// Install the plugin with the specified slug for a site given its ID
11+
case installSitePlugin(siteID: Int64, slug: String, onCompletion: (Result<Void, Error>) -> Void)
12+
13+
/// Activate the plugin with the specified name for a site given its ID
14+
case activateSitePlugin(siteID: Int64, pluginName: String, onCompletion: (Result<Void, Error>) -> Void)
15+
16+
/// Get details for the plugin with the specified name for a site given its ID
17+
case getPluginDetails(siteID: Int64, pluginName: String, onCompletion: (Result<SitePlugin, Error>) -> Void)
918
}

Yosemite/Yosemite/Stores/SitePluginStore.swift

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,12 @@ public final class SitePluginStore: Store {
2929
switch action {
3030
case .synchronizeSitePlugins(let siteID, let onCompletion):
3131
synchronizeSitePlugins(siteID: siteID, completionHandler: onCompletion)
32+
case .installSitePlugin(let siteID, let slug, let onCompletion):
33+
installSitePlugin(siteID: siteID, slug: slug, onCompletion: onCompletion)
34+
case .activateSitePlugin(let siteID, let pluginName, let onCompletion):
35+
activateSitePlugin(siteID: siteID, pluginName: pluginName, onCompletion: onCompletion)
36+
case .getPluginDetails(let siteID, let pluginName, let onCompletion):
37+
getPluginDetails(siteID: siteID, pluginName: pluginName, onCompletion: onCompletion)
3238
}
3339
}
3440
}
@@ -47,6 +53,53 @@ private extension SitePluginStore {
4753
}
4854
}
4955
}
56+
57+
func installSitePlugin(siteID: Int64, slug: String, onCompletion: @escaping (Result<Void, Error>) -> Void) {
58+
remote.installPlugin(for: siteID, slug: slug) { [weak self] result in
59+
guard let self = self else { return }
60+
switch result {
61+
case .success(let plugin):
62+
self.upsertSitePluginsInBackground(siteID: siteID, readonlyPlugins: [plugin], completionHandler: onCompletion)
63+
case .failure(let error):
64+
onCompletion(.failure(error))
65+
}
66+
}
67+
}
68+
69+
func activateSitePlugin(siteID: Int64, pluginName: String, onCompletion: @escaping (Result<Void, Error>) -> Void) {
70+
remote.activatePlugin(for: siteID, pluginName: pluginName) { [weak self] result in
71+
guard let self = self else { return }
72+
switch result {
73+
case .success(let plugin):
74+
guard plugin.status == .active else {
75+
onCompletion(.failure(SitePluginError.activationFailed))
76+
return
77+
}
78+
self.upsertSitePluginsInBackground(siteID: siteID, readonlyPlugins: [plugin], completionHandler: onCompletion)
79+
case .failure(let error):
80+
onCompletion(.failure(error))
81+
}
82+
}
83+
}
84+
85+
func getPluginDetails(siteID: Int64, pluginName: String, onCompletion: @escaping (Result<SitePlugin, Error>) -> Void) {
86+
remote.getPluginDetails(for: siteID, pluginName: pluginName) { [weak self] result in
87+
guard let self = self else { return }
88+
switch result {
89+
case .success(let plugin):
90+
self.upsertSitePluginsInBackground(siteID: siteID, readonlyPlugins: [plugin]) { result in
91+
switch result {
92+
case .success:
93+
onCompletion(.success(plugin))
94+
case .failure(let error):
95+
onCompletion(.failure(error))
96+
}
97+
}
98+
case .failure(let error):
99+
onCompletion(.failure(error))
100+
}
101+
}
102+
}
50103
}
51104

52105
// MARK: - Storage
@@ -90,3 +143,7 @@ private extension SitePluginStore {
90143
storage.deleteStalePlugins(siteID: siteID, installedPluginNames: installedPluginNames)
91144
}
92145
}
146+
147+
public enum SitePluginError: Error {
148+
case activationFailed
149+
}

Yosemite/YosemiteTests/Stores/SitePluginStoreTests.swift

Lines changed: 85 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import Fakes
55
@testable import Networking
66
@testable import Storage
77

8-
class SitePluginStoreTests: XCTestCase {
8+
final class SitePluginStoreTests: XCTestCase {
99
/// Mock Dispatcher
1010
///
1111
private var dispatcher: Dispatcher!
@@ -77,4 +77,88 @@ class SitePluginStoreTests: XCTestCase {
7777
XCTAssertEqual(viewStorage.countObjects(ofType: StorageSitePlugin.self), 5) // number of plugins in json file
7878
XCTAssertNil(viewStorage.loadPlugin(siteID: sampleSiteID, name: stalePluginName))
7979
}
80+
81+
func test_installSitePlugin_stores_plugin_correctly() {
82+
// Given
83+
network.simulateResponse(requestUrlSuffix: "plugins", filename: "plugin")
84+
let store = SitePluginStore(dispatcher: dispatcher, storageManager: storageManager, network: network)
85+
86+
// When
87+
let result: Result<Void, Error> = waitFor { promise in
88+
let action = SitePluginAction.installSitePlugin(siteID: self.sampleSiteID, slug: "jetpack") { result in
89+
promise(result)
90+
}
91+
store.onAction(action)
92+
}
93+
94+
// Then
95+
XCTAssertTrue(result.isSuccess)
96+
let plugins = viewStorage.loadPlugins(siteID: sampleSiteID)
97+
XCTAssertEqual(plugins.count, 1) // the installed plugin
98+
XCTAssertEqual(plugins.first?.plugin, "jetpack/jetpack")
99+
}
100+
101+
func test_activateSitePlugin_updates_plugin_correctly() {
102+
// Given
103+
let pluginName = "jetpack/jetpack"
104+
let plugin = SitePlugin.fake().copy(siteID: sampleSiteID, status: .inactive, name: pluginName)
105+
let storedPlugin = viewStorage.insertNewObject(ofType: StorageSitePlugin.self)
106+
storedPlugin.update(with: plugin)
107+
XCTAssertEqual(viewStorage.countObjects(ofType: StorageSitePlugin.self), 1)
108+
109+
network.simulateResponse(requestUrlSuffix: "plugins/jetpack/jetpack", filename: "plugin")
110+
let store = SitePluginStore(dispatcher: dispatcher, storageManager: storageManager, network: network)
111+
112+
// When
113+
let result: Result<Void, Error> = waitFor { promise in
114+
let action = SitePluginAction.activateSitePlugin(siteID: self.sampleSiteID, pluginName: pluginName) { result in
115+
promise(result)
116+
}
117+
store.onAction(action)
118+
}
119+
120+
// Then
121+
XCTAssertTrue(result.isSuccess)
122+
let plugins = viewStorage.loadPlugins(siteID: sampleSiteID)
123+
XCTAssertEqual(plugins.count, 1) // the installed plugin
124+
XCTAssertEqual(plugins.first?.status, SitePluginStatusEnum.active.rawValue)
125+
}
126+
127+
func test_activateSitePlugin_completes_with_failure_when_receiving_inactive_plugin() {
128+
// Given
129+
network.simulateResponse(requestUrlSuffix: "plugins/jetpack/jetpack", filename: "plugin-inactive")
130+
let store = SitePluginStore(dispatcher: dispatcher, storageManager: storageManager, network: network)
131+
132+
// When
133+
let result: Result<Void, Error> = waitFor { promise in
134+
let action = SitePluginAction.activateSitePlugin(siteID: self.sampleSiteID, pluginName: "jetpack/jetpack") { result in
135+
promise(result)
136+
}
137+
store.onAction(action)
138+
}
139+
140+
// Then
141+
XCTAssertTrue(result.isFailure)
142+
}
143+
144+
func test_getPluginDetails_stores_plugin_correctly() {
145+
// Given
146+
network.simulateResponse(requestUrlSuffix: "plugins/jetpack/jetpack", filename: "plugin")
147+
let store = SitePluginStore(dispatcher: dispatcher, storageManager: storageManager, network: network)
148+
let pluginName = "jetpack/jetpack"
149+
150+
// When
151+
let result: Result<Networking.SitePlugin, Error> = waitFor { promise in
152+
let action = SitePluginAction.getPluginDetails(siteID: self.sampleSiteID, pluginName: pluginName) { result in
153+
promise(result)
154+
}
155+
store.onAction(action)
156+
}
157+
158+
// Then
159+
XCTAssertTrue(result.isSuccess)
160+
let plugins = viewStorage.loadPlugins(siteID: sampleSiteID)
161+
XCTAssertEqual(plugins.count, 1) // the installed plugin
162+
XCTAssertEqual(plugins.first?.plugin, "jetpack/jetpack")
163+
}
80164
}

0 commit comments

Comments
 (0)