Skip to content

Commit

Permalink
Decouple PluginDirectoryServiceRemote from Alamofire (#725)
Browse files Browse the repository at this point in the history
  • Loading branch information
crazytonyli authored Feb 16, 2024
2 parents dfb2134 + dce3798 commit ae8f83e
Show file tree
Hide file tree
Showing 5 changed files with 55 additions and 89 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ _None._
### Breaking Changes

- Rewrite `WordPressOrgRestApi` to support self hosted sites and WordPress.com sites. [#724]
- Decouple `PluginDirectoryServiceRemote` from Alamofire. [#725]
- Remove `Endpoint`. [#725]

### New Features

Expand Down
4 changes: 0 additions & 4 deletions WordPressKit.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -607,7 +607,6 @@
E14694031F344F71004052C8 /* site-plugins-error.json in Resources */ = {isa = PBXBuildFile; fileRef = E14694021F344F71004052C8 /* site-plugins-error.json */; };
E1787DB0200E564B004CB3AF /* timezones.json in Resources */ = {isa = PBXBuildFile; fileRef = E1787DAF200E564B004CB3AF /* timezones.json */; };
E1787DB2200E5690004CB3AF /* TimeZoneServiceRemoteTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1787DB1200E5690004CB3AF /* TimeZoneServiceRemoteTests.swift */; };
E182BF6A1FD961810001D850 /* Endpoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = E182BF691FD961810001D850 /* Endpoint.swift */; };
E194CB731FBDEF6500B0A8B8 /* PluginState.swift in Sources */ = {isa = PBXBuildFile; fileRef = E194CB721FBDEF6400B0A8B8 /* PluginState.swift */; };
E1A6605F1FD694ED00BAC339 /* PluginDirectoryEntry.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1A6605E1FD694ED00BAC339 /* PluginDirectoryEntry.swift */; };
E1BD95151FD5A2B800CD5CE3 /* PluginDirectoryServiceRemote.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1BD95141FD5A2B800CD5CE3 /* PluginDirectoryServiceRemote.swift */; };
Expand Down Expand Up @@ -1336,7 +1335,6 @@
E14694021F344F71004052C8 /* site-plugins-error.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "site-plugins-error.json"; sourceTree = "<group>"; };
E1787DAF200E564B004CB3AF /* timezones.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = timezones.json; sourceTree = "<group>"; };
E1787DB1200E5690004CB3AF /* TimeZoneServiceRemoteTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimeZoneServiceRemoteTests.swift; sourceTree = "<group>"; };
E182BF691FD961810001D850 /* Endpoint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Endpoint.swift; sourceTree = "<group>"; };
E194CB721FBDEF6400B0A8B8 /* PluginState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PluginState.swift; sourceTree = "<group>"; };
E1A6605E1FD694ED00BAC339 /* PluginDirectoryEntry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PluginDirectoryEntry.swift; sourceTree = "<group>"; };
E1BD95141FD5A2B800CD5CE3 /* PluginDirectoryServiceRemote.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PluginDirectoryServiceRemote.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1985,7 +1983,6 @@
74BA04F11F06DC0A00ED5CD8 /* CommentServiceRemoteXMLRPC.m */,
8BB5F62027A99A2000B2FFAF /* DashboardServiceRemote.swift */,
7E0D64FE22D855700092AD10 /* EditorServiceRemote.swift */,
E182BF691FD961810001D850 /* Endpoint.swift */,
F9E56DF724EB125600916770 /* FeatureFlagRemote.swift */,
74650F711F0EA1A700188EDB /* GravatarServiceRemote.swift */,
1769DEA924729AFF00F42EFC /* HomepageSettingsServiceRemote.swift */,
Expand Down Expand Up @@ -3462,7 +3459,6 @@
F1BB7806240FB90B0030ADDC /* AtomicAuthenticationServiceRemote.swift in Sources */,
404057CE221C38130060250C /* StatsTopVideosTimeIntervalData.swift in Sources */,
7E0D64FF22D855700092AD10 /* EditorServiceRemote.swift in Sources */,
E182BF6A1FD961810001D850 /* Endpoint.swift in Sources */,
9AF4F2FF2183346B00570E4B /* RemoteRevision.swift in Sources */,
17D936252475D8AB008B2205 /* RemoteHomepageType.swift in Sources */,
74BA04F41F06DC0A00ED5CD8 /* CommentServiceRemoteREST.m in Sources */,
Expand Down
55 changes: 0 additions & 55 deletions WordPressKit/Endpoint.swift

This file was deleted.

54 changes: 28 additions & 26 deletions WordPressKit/PluginDirectoryServiceRemote.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import Foundation
import Alamofire

private struct PluginDirectoryRemoteConstants {
static let dateFormatter: DateFormatter = {
Expand Down Expand Up @@ -47,7 +46,7 @@ public enum PluginDirectoryFeedType: Hashable {
}
}

public struct PluginDirectoryGetInformationEndpoint: Endpoint {
public struct PluginDirectoryGetInformationEndpoint {
public enum Error: Swift.Error {
case pluginNotFound
}
Expand All @@ -57,20 +56,18 @@ public struct PluginDirectoryGetInformationEndpoint: Endpoint {
self.slug = slug
}

public func buildRequest() throws -> URLRequest {
let url = PluginDirectoryRemoteConstants.getInformationEndpoint
.appendingPathComponent(slug)
.appendingPathExtension("json")
let request = URLRequest(url: url)
let encodedRequest = try URLEncoding.default.encode(request, with: ["fields": "icons,banners"])
return encodedRequest
func buildRequest() throws -> URLRequest {
try HTTPRequestBuilder(url: PluginDirectoryRemoteConstants.getInformationEndpoint)
.append(percentEncodedPath: "\(slug).json")
.query(name: "fields", value: "icons,banners")
.build()
}

public func parseResponse(data: Data) throws -> PluginDirectoryEntry {
func parseResponse(data: Data) throws -> PluginDirectoryEntry {
return try PluginDirectoryRemoteConstants.jsonDecoder.decode(PluginDirectoryEntry.self, from: data)
}

public func validate(request: URLRequest?, response: HTTPURLResponse, data: Data?) throws {
func validate(response: HTTPURLResponse, data: Data?) throws {
// api.wordpress.org has an odd way of responding to plugin info requests for
// plugins not in the directory: it will return `null` with an HTTP 200 OK.
// This turns that case into a `.pluginNotFound` error.
Expand All @@ -83,20 +80,20 @@ public struct PluginDirectoryGetInformationEndpoint: Endpoint {
}
}

public struct PluginDirectoryFeedEndpoint: Endpoint {
public struct PluginDirectoryFeedEndpoint {
public enum Error: Swift.Error {
case genericError
}

let feedType: PluginDirectoryFeedType
let pageNumber: Int

public init(feedType: PluginDirectoryFeedType) {
init(feedType: PluginDirectoryFeedType) {
self.feedType = feedType
self.pageNumber = 1
}

public func buildRequest() throws -> URLRequest {
func buildRequest() throws -> URLRequest {
var parameters: [String: Any] = ["action": "query_plugins",
"request[per_page]": PluginDirectoryRemoteConstants.pluginsPerPage,
"request[fields][icons]": 1,
Expand All @@ -113,17 +110,16 @@ public struct PluginDirectoryFeedEndpoint: Endpoint {

}

let request = URLRequest(url: PluginDirectoryRemoteConstants.feedEndpoint)
let encodedRequest = try URLEncoding.default.encode(request, with: parameters)

return encodedRequest
return try HTTPRequestBuilder(url: PluginDirectoryRemoteConstants.feedEndpoint)
.query(parameters)
.build()
}

public func parseResponse(data: Data) throws -> PluginDirectoryFeedPage {
func parseResponse(data: Data) throws -> PluginDirectoryFeedPage {
return try PluginDirectoryRemoteConstants.jsonDecoder.decode(PluginDirectoryFeedPage.self, from: data)
}

public func validate(request: URLRequest?, response: HTTPURLResponse, data: Data?) throws {
func validate(response: HTTPURLResponse, data: Data?) throws {
if response.statusCode != 200 { throw Error.genericError}
}
}
Expand All @@ -132,13 +128,19 @@ public struct PluginDirectoryServiceRemote {

public init() {}

public func getPluginFeed(_ feedType: PluginDirectoryFeedType,
pageNumber: Int = 1,
completion: @escaping (Result<PluginDirectoryFeedPage>) -> Void) {
PluginDirectoryFeedEndpoint(feedType: feedType).request(completion: completion)
public func getPluginFeed(_ feedType: PluginDirectoryFeedType, pageNumber: Int = 1) async throws -> PluginDirectoryFeedPage {
let endpoint = PluginDirectoryFeedEndpoint(feedType: feedType)
let (data, response) = try await URLSession.shared.data(for: endpoint.buildRequest())
let httpResponse = response as! HTTPURLResponse
try endpoint.validate(response: httpResponse, data: data)
return try endpoint.parseResponse(data: data)
}

public func getPluginInformation(slug: String, completion: @escaping (Result<PluginDirectoryEntry>) -> Void) {
PluginDirectoryGetInformationEndpoint(slug: slug).request(completion: completion)
public func getPluginInformation(slug: String) async throws -> PluginDirectoryEntry {
let endpoint = PluginDirectoryGetInformationEndpoint(slug: slug)
let (data, response) = try await URLSession.shared.data(for: endpoint.buildRequest())
let httpResponse = response as! HTTPURLResponse
try endpoint.validate(response: httpResponse, data: data)
return try endpoint.parseResponse(data: data)
}
}
29 changes: 25 additions & 4 deletions WordPressKitTests/PluginDirectoryTests.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import XCTest
import OHHTTPStubs
@testable import WordPressKit

class PluginDirectoryTests: XCTestCase {
Expand Down Expand Up @@ -51,13 +52,33 @@ class PluginDirectoryTests: XCTestCase {
}
}

func testGetPluginInformation() async throws {
let data = try MockPluginDirectoryProvider.getPluginDirectoryMockData(with: "plugin-directory-rename-xml-rpc", sender: type(of: self))
stub(condition: isHost("api.wordpress.org")) { _ in
HTTPStubsResponse(data: data, statusCode: 200, headers: ["Content-Type": "application/json"])
}

let plugin = try await PluginDirectoryServiceRemote().getPluginInformation(slug: "rename-xml-rpc")
XCTAssertEqual(plugin.name, "Rename XMLRPC")
}

func testGetDirectoryFeed() async throws {
let data = try MockPluginDirectoryProvider.getPluginDirectoryMockData(with: "plugin-directory-popular", sender: type(of: self))
stub(condition: isHost("api.wordpress.org")) { _ in
HTTPStubsResponse(data: data, statusCode: 200, headers: ["Content-Type": "application/json"])
}

let feed = try await PluginDirectoryServiceRemote().getPluginFeed(.popular)
XCTAssertEqual(feed.plugins.first?.name, "Contact Form 7")
}

func testValidateResponseFound() {
let data = try! MockPluginDirectoryProvider.getPluginDirectoryMockData(with: "plugin-directory-rename-xml-rpc", sender: type(of: self))
let endpoint = PluginDirectoryGetInformationEndpoint(slug: "jetpack")
do {
let request = try endpoint.buildRequest()
let response = HTTPURLResponse(url: request.url!, statusCode: 200, httpVersion: "1.1", headerFields: nil)!
XCTAssertNoThrow(try endpoint.validate(request: request, response: response, data: data))
XCTAssertNoThrow(try endpoint.validate(response: response, data: data))
} catch {
XCTFail(error.localizedDescription)
}
Expand All @@ -70,7 +91,7 @@ class PluginDirectoryTests: XCTestCase {
let request = try! endpoint.buildRequest()
let response = HTTPURLResponse(url: request.url!, statusCode: 200, httpVersion: "1.1", headerFields: nil)!

XCTAssertThrowsError(try endpoint.validate(request: request, response: response, data: "null".data(using: .utf8)))
XCTAssertThrowsError(try endpoint.validate(response: response, data: "null".data(using: .utf8)))
}

func testValidatePluginDirectoryFeedResponseSucceeds() throws {
Expand All @@ -79,7 +100,7 @@ class PluginDirectoryTests: XCTestCase {
let request = try endpoint.buildRequest()
let response = HTTPURLResponse(url: request.url!, statusCode: 200, httpVersion: "1.1", headerFields: nil)!

XCTAssertNoThrow(try endpoint.validate(request: request, response: response, data: "null".data(using: .utf8)))
XCTAssertNoThrow(try endpoint.validate(response: response, data: "null".data(using: .utf8)))
}

func testValidatePluginDirectoryFeedResponseFails() {
Expand All @@ -88,7 +109,7 @@ class PluginDirectoryTests: XCTestCase {
let request = try! endpoint.buildRequest()
let response = HTTPURLResponse(url: request.url!, statusCode: 403, httpVersion: "1.1", headerFields: nil)!

XCTAssertThrowsError(try endpoint.validate(request: request, response: response, data: "null".data(using: .utf8)))
XCTAssertThrowsError(try endpoint.validate(response: response, data: "null".data(using: .utf8)))
}

func testNewDirectoryFeedRequest() {
Expand Down

0 comments on commit ae8f83e

Please sign in to comment.