Skip to content
This repository was archived by the owner on Sep 15, 2025. It is now read-only.

Commit 92dc500

Browse files
authored
Merge pull request #30 from wordpress-mobile/feature/automated-transfer
Support for Automated Transfer.
2 parents 212c261 + 6e5abb2 commit 92dc500

File tree

4 files changed

+185
-1
lines changed

4 files changed

+185
-1
lines changed

WordPressKit.podspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Pod::Spec.new do |s|
22
s.name = "WordPressKit"
3-
s.version = "1.3.0"
3+
s.version = "1.4.0-beta.1"
44
s.summary = "WordPressKit offers a clean and simple WordPress.com and WordPress.org API."
55

66
s.description = <<-DESC

WordPressKit.xcodeproj/project.pbxproj

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
17CE77F120C6EB41001DEA5A /* ReaderFeed.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17CE77F020C6EB41001DEA5A /* ReaderFeed.swift */; };
1717
17CE77F420C701C8001DEA5A /* ReaderSiteSearchServiceRemoteTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17CE77F320C701C8001DEA5A /* ReaderSiteSearchServiceRemoteTests.swift */; };
1818
240315B0A1D6C2B981572B5B /* Pods_WordPressKitTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ED05C8FF3E61D93CE5BA527E /* Pods_WordPressKitTests.framework */; };
19+
40247DFA2120D8E100AE1C3C /* AutomatedTransferService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40247DF92120D8E100AE1C3C /* AutomatedTransferService.swift */; };
20+
40247DFC2120E69600AE1C3C /* AutomatedTransferStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40247DFB2120E69600AE1C3C /* AutomatedTransferStatus.swift */; };
1921
40AB1ADA200FED25009B533D /* PluginDirectoryFeedPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40AB1AD9200FED25009B533D /* PluginDirectoryFeedPage.swift */; };
2022
40E4698B2017C2840030DB5F /* plugin-directory-popular.json in Resources */ = {isa = PBXBuildFile; fileRef = 40E4698A2017C2840030DB5F /* plugin-directory-popular.json */; };
2123
40E4698D2017D2E30030DB5F /* plugin-directory-new.json in Resources */ = {isa = PBXBuildFile; fileRef = 40E4698C2017D2E30030DB5F /* plugin-directory-new.json */; };
@@ -433,6 +435,8 @@
433435
17CE77F020C6EB41001DEA5A /* ReaderFeed.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReaderFeed.swift; sourceTree = "<group>"; };
434436
17CE77F320C701C8001DEA5A /* ReaderSiteSearchServiceRemoteTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReaderSiteSearchServiceRemoteTests.swift; sourceTree = "<group>"; };
435437
264F5C834541BBF2018F4964 /* Pods-WordPressKitTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WordPressKitTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-WordPressKitTests/Pods-WordPressKitTests.debug.xcconfig"; sourceTree = "<group>"; };
438+
40247DF92120D8E100AE1C3C /* AutomatedTransferService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutomatedTransferService.swift; sourceTree = "<group>"; };
439+
40247DFB2120E69600AE1C3C /* AutomatedTransferStatus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutomatedTransferStatus.swift; sourceTree = "<group>"; };
436440
40AB1AD9200FED25009B533D /* PluginDirectoryFeedPage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PluginDirectoryFeedPage.swift; sourceTree = "<group>"; };
437441
40E4698A2017C2840030DB5F /* plugin-directory-popular.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "plugin-directory-popular.json"; sourceTree = "<group>"; };
438442
40E4698C2017D2E30030DB5F /* plugin-directory-new.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "plugin-directory-new.json"; sourceTree = "<group>"; };
@@ -1135,6 +1139,7 @@
11351139
93BD273A1EE73282002BB00B /* AccountServiceRemoteREST.m */,
11361140
7403A2E31EF06ED500DED7DC /* AccountSettingsRemote.swift */,
11371141
826016F01F9FA13A00533B6C /* ActivityServiceRemote.swift */,
1142+
40247DF92120D8E100AE1C3C /* AutomatedTransferService.swift */,
11381143
82FFBF551F460DD400F4573F /* BlogJetpackSettingsServiceRemote.swift */,
11391144
74B5F0DB1EF829B800B411E7 /* BlogServiceRemote.h */,
11401145
74B5F0D31EF8299B00B411E7 /* BlogServiceRemoteREST.h */,
@@ -1219,6 +1224,7 @@
12191224
7403A3011EF0726E00DED7DC /* AccountSettings.swift */,
12201225
74E229591F1E77290085F7F2 /* KeyringConnection.swift */,
12211226
74E2295A1F1E77290085F7F2 /* KeyringConnectionExternalUser.swift */,
1227+
40247DFB2120E69600AE1C3C /* AutomatedTransferStatus.swift */,
12221228
826016F21F9FA17B00533B6C /* Activity.swift */,
12231229
93C674E51EE8345300BFAF05 /* RemoteBlog.h */,
12241230
93C674E61EE8345300BFAF05 /* RemoteBlog.m */,
@@ -2095,7 +2101,9 @@
20952101
9311A6861F22625A00704AC9 /* RemoteTaxonomyPaging.m in Sources */,
20962102
93BD27821EE73944002BB00B /* WordPressOrgXMLRPCValidator.swift in Sources */,
20972103
748710A81F06B542008095AB /* RemotePlan.swift in Sources */,
2104+
40247DFC2120E69600AE1C3C /* AutomatedTransferStatus.swift in Sources */,
20982105
740B23C51F17EE8000067A2A /* RemotePost.m in Sources */,
2106+
40247DFA2120D8E100AE1C3C /* AutomatedTransferService.swift in Sources */,
20992107
74E2295B1F1E77290085F7F2 /* KeyringConnection.swift in Sources */,
21002108
74B5F0DA1EF8299B00B411E7 /* BlogServiceRemoteXMLRPC.m in Sources */,
21012109
E1EF5D5D1F9F329900B6D53E /* SitePluginCapabilities.swift in Sources */,
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
import Foundation
2+
3+
/// Class encapsualting all requests related to performing Automated Transfer operations.
4+
public class AutomatedTransferService: ServiceRemoteWordPressComREST {
5+
6+
public enum ResponseError: Error {
7+
case decodingFailure
8+
}
9+
10+
public enum AutomatedTransferEligibilityError: Error {
11+
case unverifiedEmail
12+
case excessiveDiskSpaceUsage
13+
case noBusinessPlan
14+
case VIPSite
15+
case notAdmin
16+
case notDomainOwner
17+
case noCustomDomain
18+
case greylistedSite
19+
case privateSite
20+
case unknown
21+
}
22+
23+
public func checkTransferEligibility(siteID: Int,
24+
success: @escaping () -> Void,
25+
failure: @escaping (AutomatedTransferEligibilityError) -> Void) {
26+
let endpoint = "sites/\(siteID)/automated-transfers/eligibility"
27+
let path = self.path(forEndpoint: endpoint, withVersion: ._1_1)
28+
29+
wordPressComRestApi.GET(path, parameters: nil, success: { (responseObject, httpResponse) in
30+
guard let response = responseObject as? [String: AnyObject] else {
31+
failure(.unknown)
32+
return
33+
}
34+
35+
guard let isEligible = response["is_eligible"] as? Bool, isEligible == true else {
36+
failure(self.eligibilityError(from: response))
37+
return
38+
}
39+
40+
success()
41+
}, failure: { _, _ in
42+
failure(.unknown)
43+
})
44+
}
45+
46+
public typealias AutomatedTransferInitationResponse = (transferID: Int, status: AutomatedTransferStatus)
47+
public func initiateAutomatedTransfer(siteID: Int,
48+
pluginSlug: String,
49+
success: @escaping (AutomatedTransferInitationResponse) -> Void,
50+
failure: @escaping (Error) -> Void) {
51+
52+
let endpoint = "sites/\(siteID)/automated-transfers/initiate"
53+
let path = self.path(forEndpoint: endpoint, withVersion: ._1_1)
54+
let payload = ["plugin": pluginSlug] as [String: AnyObject]
55+
56+
wordPressComRestApi.POST(path, parameters: payload, success: { (responseObject, httpResponse) in
57+
guard let response = responseObject as? [String: AnyObject] else {
58+
failure(ResponseError.decodingFailure)
59+
return
60+
}
61+
62+
guard let transferID = response["transfer_id"] as? Int,
63+
let status = response["status"] as? String,
64+
let statusObject = AutomatedTransferStatus(status: status) else {
65+
failure(ResponseError.decodingFailure)
66+
return
67+
}
68+
69+
success((transferID: transferID, status: statusObject))
70+
}) { (error, _) in
71+
failure(error)
72+
}
73+
74+
}
75+
76+
public func fetchAutomatedTransferStatus(siteID: Int,
77+
success: @escaping (AutomatedTransferStatus) -> Void,
78+
failure: @escaping (Error) -> Void) {
79+
80+
let endpoint = "sites/\(siteID)/automated-transfers/status"
81+
let path = self.path(forEndpoint: endpoint, withVersion: ._1_1)
82+
83+
wordPressComRestApi.GET(path, parameters: nil, success: { (responseObject, httpResponse) in
84+
guard let response = responseObject as? [String: AnyObject] else {
85+
failure(ResponseError.decodingFailure)
86+
return
87+
}
88+
89+
guard let status = response["status"] as? String,
90+
let currentStep = response["step"] as? Int,
91+
let totalSteps = response["total"] as? Int,
92+
let statusObject = AutomatedTransferStatus(status: status, step: currentStep, totalSteps: totalSteps) else {
93+
failure(ResponseError.decodingFailure)
94+
return
95+
}
96+
97+
success(statusObject)
98+
}) { (error, _) in
99+
failure(error)
100+
}
101+
102+
}
103+
104+
private func eligibilityError(from response: [String: AnyObject]) -> AutomatedTransferEligibilityError {
105+
guard let errors = response["errors"] as? [[String: AnyObject]],
106+
let errorType = errors.first?["code"] as? String else {
107+
// The API can potentially return multiple errors here. Since there isn't really an actionable
108+
// way for user to deal with multiple of them at once, we're just picking the first one.
109+
return .unknown
110+
}
111+
112+
switch errorType {
113+
case "email_unverified":
114+
return .unverifiedEmail
115+
case "excessive_disk_space":
116+
return .excessiveDiskSpaceUsage
117+
case "no_business_plan":
118+
return .noBusinessPlan
119+
case "no_vip_sites":
120+
return .VIPSite
121+
case "non_admin_user":
122+
return .notAdmin
123+
case "not_domain_owner":
124+
return .notDomainOwner
125+
case "not_using_custom_domain":
126+
return .noCustomDomain
127+
case "site_graylisted":
128+
return .greylistedSite
129+
case "site_private":
130+
return .privateSite
131+
default:
132+
return .unknown
133+
}
134+
}
135+
136+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import Foundation
2+
3+
/// A helper object encapsulating a status of Automated Transfer operation.
4+
public struct AutomatedTransferStatus {
5+
public enum State: String, RawRepresentable {
6+
case active
7+
case backfilling
8+
case complete
9+
case error
10+
case notFound = "not found"
11+
case unknownStatus = "unknown_status"
12+
case uploading
13+
case pending
14+
}
15+
16+
public let status: State
17+
public let step: Int?
18+
public let totalSteps: Int?
19+
20+
init?(status statusString: String) {
21+
guard let status = State(rawValue: statusString) else {
22+
return nil
23+
}
24+
25+
self.status = status
26+
self.step = nil
27+
self.totalSteps = nil
28+
}
29+
30+
init?(status statusString: String, step: Int, totalSteps: Int) {
31+
guard let status = State(rawValue: statusString) else {
32+
return nil
33+
}
34+
35+
self.status = status
36+
self.step = step
37+
self.totalSteps = totalSteps
38+
}
39+
40+
}

0 commit comments

Comments
 (0)