Skip to content

Commit

Permalink
Merge pull request #30 from wordpress-mobile/feature/automated-transfer
Browse files Browse the repository at this point in the history
Support for Automated Transfer.
  • Loading branch information
jklausa authored Aug 30, 2018
2 parents 212c261 + 6e5abb2 commit 92dc500
Show file tree
Hide file tree
Showing 4 changed files with 185 additions and 1 deletion.
2 changes: 1 addition & 1 deletion WordPressKit.podspec
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "WordPressKit"
s.version = "1.3.0"
s.version = "1.4.0-beta.1"
s.summary = "WordPressKit offers a clean and simple WordPress.com and WordPress.org API."

s.description = <<-DESC
Expand Down
8 changes: 8 additions & 0 deletions WordPressKit.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
17CE77F120C6EB41001DEA5A /* ReaderFeed.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17CE77F020C6EB41001DEA5A /* ReaderFeed.swift */; };
17CE77F420C701C8001DEA5A /* ReaderSiteSearchServiceRemoteTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17CE77F320C701C8001DEA5A /* ReaderSiteSearchServiceRemoteTests.swift */; };
240315B0A1D6C2B981572B5B /* Pods_WordPressKitTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ED05C8FF3E61D93CE5BA527E /* Pods_WordPressKitTests.framework */; };
40247DFA2120D8E100AE1C3C /* AutomatedTransferService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40247DF92120D8E100AE1C3C /* AutomatedTransferService.swift */; };
40247DFC2120E69600AE1C3C /* AutomatedTransferStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40247DFB2120E69600AE1C3C /* AutomatedTransferStatus.swift */; };
40AB1ADA200FED25009B533D /* PluginDirectoryFeedPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40AB1AD9200FED25009B533D /* PluginDirectoryFeedPage.swift */; };
40E4698B2017C2840030DB5F /* plugin-directory-popular.json in Resources */ = {isa = PBXBuildFile; fileRef = 40E4698A2017C2840030DB5F /* plugin-directory-popular.json */; };
40E4698D2017D2E30030DB5F /* plugin-directory-new.json in Resources */ = {isa = PBXBuildFile; fileRef = 40E4698C2017D2E30030DB5F /* plugin-directory-new.json */; };
Expand Down Expand Up @@ -433,6 +435,8 @@
17CE77F020C6EB41001DEA5A /* ReaderFeed.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReaderFeed.swift; sourceTree = "<group>"; };
17CE77F320C701C8001DEA5A /* ReaderSiteSearchServiceRemoteTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReaderSiteSearchServiceRemoteTests.swift; sourceTree = "<group>"; };
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>"; };
40247DF92120D8E100AE1C3C /* AutomatedTransferService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutomatedTransferService.swift; sourceTree = "<group>"; };
40247DFB2120E69600AE1C3C /* AutomatedTransferStatus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutomatedTransferStatus.swift; sourceTree = "<group>"; };
40AB1AD9200FED25009B533D /* PluginDirectoryFeedPage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PluginDirectoryFeedPage.swift; sourceTree = "<group>"; };
40E4698A2017C2840030DB5F /* plugin-directory-popular.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "plugin-directory-popular.json"; sourceTree = "<group>"; };
40E4698C2017D2E30030DB5F /* plugin-directory-new.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "plugin-directory-new.json"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1135,6 +1139,7 @@
93BD273A1EE73282002BB00B /* AccountServiceRemoteREST.m */,
7403A2E31EF06ED500DED7DC /* AccountSettingsRemote.swift */,
826016F01F9FA13A00533B6C /* ActivityServiceRemote.swift */,
40247DF92120D8E100AE1C3C /* AutomatedTransferService.swift */,
82FFBF551F460DD400F4573F /* BlogJetpackSettingsServiceRemote.swift */,
74B5F0DB1EF829B800B411E7 /* BlogServiceRemote.h */,
74B5F0D31EF8299B00B411E7 /* BlogServiceRemoteREST.h */,
Expand Down Expand Up @@ -1219,6 +1224,7 @@
7403A3011EF0726E00DED7DC /* AccountSettings.swift */,
74E229591F1E77290085F7F2 /* KeyringConnection.swift */,
74E2295A1F1E77290085F7F2 /* KeyringConnectionExternalUser.swift */,
40247DFB2120E69600AE1C3C /* AutomatedTransferStatus.swift */,
826016F21F9FA17B00533B6C /* Activity.swift */,
93C674E51EE8345300BFAF05 /* RemoteBlog.h */,
93C674E61EE8345300BFAF05 /* RemoteBlog.m */,
Expand Down Expand Up @@ -2095,7 +2101,9 @@
9311A6861F22625A00704AC9 /* RemoteTaxonomyPaging.m in Sources */,
93BD27821EE73944002BB00B /* WordPressOrgXMLRPCValidator.swift in Sources */,
748710A81F06B542008095AB /* RemotePlan.swift in Sources */,
40247DFC2120E69600AE1C3C /* AutomatedTransferStatus.swift in Sources */,
740B23C51F17EE8000067A2A /* RemotePost.m in Sources */,
40247DFA2120D8E100AE1C3C /* AutomatedTransferService.swift in Sources */,
74E2295B1F1E77290085F7F2 /* KeyringConnection.swift in Sources */,
74B5F0DA1EF8299B00B411E7 /* BlogServiceRemoteXMLRPC.m in Sources */,
E1EF5D5D1F9F329900B6D53E /* SitePluginCapabilities.swift in Sources */,
Expand Down
136 changes: 136 additions & 0 deletions WordPressKit/AutomatedTransferService.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
import Foundation

/// Class encapsualting all requests related to performing Automated Transfer operations.
public class AutomatedTransferService: ServiceRemoteWordPressComREST {

public enum ResponseError: Error {
case decodingFailure
}

public enum AutomatedTransferEligibilityError: Error {
case unverifiedEmail
case excessiveDiskSpaceUsage
case noBusinessPlan
case VIPSite
case notAdmin
case notDomainOwner
case noCustomDomain
case greylistedSite
case privateSite
case unknown
}

public func checkTransferEligibility(siteID: Int,
success: @escaping () -> Void,
failure: @escaping (AutomatedTransferEligibilityError) -> Void) {
let endpoint = "sites/\(siteID)/automated-transfers/eligibility"
let path = self.path(forEndpoint: endpoint, withVersion: ._1_1)

wordPressComRestApi.GET(path, parameters: nil, success: { (responseObject, httpResponse) in
guard let response = responseObject as? [String: AnyObject] else {
failure(.unknown)
return
}

guard let isEligible = response["is_eligible"] as? Bool, isEligible == true else {
failure(self.eligibilityError(from: response))
return
}

success()
}, failure: { _, _ in
failure(.unknown)
})
}

public typealias AutomatedTransferInitationResponse = (transferID: Int, status: AutomatedTransferStatus)
public func initiateAutomatedTransfer(siteID: Int,
pluginSlug: String,
success: @escaping (AutomatedTransferInitationResponse) -> Void,
failure: @escaping (Error) -> Void) {

let endpoint = "sites/\(siteID)/automated-transfers/initiate"
let path = self.path(forEndpoint: endpoint, withVersion: ._1_1)
let payload = ["plugin": pluginSlug] as [String: AnyObject]

wordPressComRestApi.POST(path, parameters: payload, success: { (responseObject, httpResponse) in
guard let response = responseObject as? [String: AnyObject] else {
failure(ResponseError.decodingFailure)
return
}

guard let transferID = response["transfer_id"] as? Int,
let status = response["status"] as? String,
let statusObject = AutomatedTransferStatus(status: status) else {
failure(ResponseError.decodingFailure)
return
}

success((transferID: transferID, status: statusObject))
}) { (error, _) in
failure(error)
}

}

public func fetchAutomatedTransferStatus(siteID: Int,
success: @escaping (AutomatedTransferStatus) -> Void,
failure: @escaping (Error) -> Void) {

let endpoint = "sites/\(siteID)/automated-transfers/status"
let path = self.path(forEndpoint: endpoint, withVersion: ._1_1)

wordPressComRestApi.GET(path, parameters: nil, success: { (responseObject, httpResponse) in
guard let response = responseObject as? [String: AnyObject] else {
failure(ResponseError.decodingFailure)
return
}

guard let status = response["status"] as? String,
let currentStep = response["step"] as? Int,
let totalSteps = response["total"] as? Int,
let statusObject = AutomatedTransferStatus(status: status, step: currentStep, totalSteps: totalSteps) else {
failure(ResponseError.decodingFailure)
return
}

success(statusObject)
}) { (error, _) in
failure(error)
}

}

private func eligibilityError(from response: [String: AnyObject]) -> AutomatedTransferEligibilityError {
guard let errors = response["errors"] as? [[String: AnyObject]],
let errorType = errors.first?["code"] as? String else {
// The API can potentially return multiple errors here. Since there isn't really an actionable
// way for user to deal with multiple of them at once, we're just picking the first one.
return .unknown
}

switch errorType {
case "email_unverified":
return .unverifiedEmail
case "excessive_disk_space":
return .excessiveDiskSpaceUsage
case "no_business_plan":
return .noBusinessPlan
case "no_vip_sites":
return .VIPSite
case "non_admin_user":
return .notAdmin
case "not_domain_owner":
return .notDomainOwner
case "not_using_custom_domain":
return .noCustomDomain
case "site_graylisted":
return .greylistedSite
case "site_private":
return .privateSite
default:
return .unknown
}
}

}
40 changes: 40 additions & 0 deletions WordPressKit/AutomatedTransferStatus.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import Foundation

/// A helper object encapsulating a status of Automated Transfer operation.
public struct AutomatedTransferStatus {
public enum State: String, RawRepresentable {
case active
case backfilling
case complete
case error
case notFound = "not found"
case unknownStatus = "unknown_status"
case uploading
case pending
}

public let status: State
public let step: Int?
public let totalSteps: Int?

init?(status statusString: String) {
guard let status = State(rawValue: statusString) else {
return nil
}

self.status = status
self.step = nil
self.totalSteps = nil
}

init?(status statusString: String, step: Int, totalSteps: Int) {
guard let status = State(rawValue: statusString) else {
return nil
}

self.status = status
self.step = step
self.totalSteps = totalSteps
}

}

0 comments on commit 92dc500

Please sign in to comment.