Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions Networking/Networking.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@
B567AF2F20A0FB8F00AB6C62 /* AuthenticatedRequestTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B567AF2C20A0FB8F00AB6C62 /* AuthenticatedRequestTests.swift */; };
B567AF3020A0FB8F00AB6C62 /* DotcomRequestTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B567AF2D20A0FB8F00AB6C62 /* DotcomRequestTests.swift */; };
B567AF3120A0FB8F00AB6C62 /* JetpackRequestTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B567AF2E20A0FB8F00AB6C62 /* JetpackRequestTests.swift */; };
B56C1EB620EA757B00D749F9 /* SiteListMapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = B56C1EB520EA757B00D749F9 /* SiteListMapper.swift */; };
B56C1EB820EA76F500D749F9 /* Site.swift in Sources */ = {isa = PBXBuildFile; fileRef = B56C1EB720EA76F500D749F9 /* Site.swift */; };
B56C1EBA20EA7D2C00D749F9 /* sites.json in Resources */ = {isa = PBXBuildFile; fileRef = B56C1EB920EA7D2C00D749F9 /* sites.json */; };
B5969E1520A47F99005E9DF1 /* RemoteTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5969E1420A47F99005E9DF1 /* RemoteTests.swift */; };
B5BB1D0C20A2050300112D92 /* DateFormatter+Woo.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5BB1D0B20A2050300112D92 /* DateFormatter+Woo.swift */; };
B5BB1D1020A237FB00112D92 /* Address.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5BB1D0F20A237FB00112D92 /* Address.swift */; };
Expand Down Expand Up @@ -113,6 +116,9 @@
B567AF2C20A0FB8F00AB6C62 /* AuthenticatedRequestTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AuthenticatedRequestTests.swift; sourceTree = "<group>"; };
B567AF2D20A0FB8F00AB6C62 /* DotcomRequestTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DotcomRequestTests.swift; sourceTree = "<group>"; };
B567AF2E20A0FB8F00AB6C62 /* JetpackRequestTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JetpackRequestTests.swift; sourceTree = "<group>"; };
B56C1EB520EA757B00D749F9 /* SiteListMapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SiteListMapper.swift; sourceTree = "<group>"; };
B56C1EB720EA76F500D749F9 /* Site.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Site.swift; sourceTree = "<group>"; };
B56C1EB920EA7D2C00D749F9 /* sites.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = sites.json; sourceTree = "<group>"; };
B5969E1420A47F99005E9DF1 /* RemoteTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteTests.swift; sourceTree = "<group>"; };
B5BB1D0B20A2050300112D92 /* DateFormatter+Woo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DateFormatter+Woo.swift"; sourceTree = "<group>"; };
B5BB1D0F20A237FB00112D92 /* Address.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Address.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -298,6 +304,7 @@
B5C6FCCE20A3592900A4F8E4 /* OrderItem.swift */,
74C8F06320EEB44800B6EDC9 /* OrderNote.swift */,
B5BB1D1120A255EC00112D92 /* OrderStatus.swift */,
B56C1EB720EA76F500D749F9 /* Site.swift */,
);
path = Model;
sourceTree = "<group>";
Expand All @@ -312,6 +319,7 @@
74C8F06B20EEBD5D00B6EDC9 /* broken-order.json */,
74C8F06520EEB76400B6EDC9 /* order-notes.json */,
74C8F06F20EEC3A800B6EDC9 /* broken-notes.json */,
B56C1EB920EA7D2C00D749F9 /* sites.json */,
);
path = Responses;
sourceTree = "<group>";
Expand All @@ -323,6 +331,7 @@
B505F6CC20BEE37E00BB1B69 /* AccountMapper.swift */,
B5C6FCD320A373BA00A4F8E4 /* OrderMapper.swift */,
B567AF2A20A0FA4200AB6C62 /* OrderListMapper.swift */,
B56C1EB520EA757B00D749F9 /* SiteListMapper.swift */,
74C8F06720EEB7BC00B6EDC9 /* OrderNotesMapper.swift */,
);
path = Mapper;
Expand Down Expand Up @@ -473,6 +482,7 @@
B505F6D520BEE4E700BB1B69 /* me.json in Resources */,
B5C6FCD620A3768900A4F8E4 /* order.json in Resources */,
B559EBAA20A0B5CD00836CD4 /* orders-load-all.json in Resources */,
B56C1EBA20EA7D2C00D749F9 /* sites.json in Resources */,
CE20179320E3EFA7005B4C18 /* broken-orders.json in Resources */,
74C8F07020EEC3A800B6EDC9 /* broken-notes.json in Resources */,
);
Expand Down Expand Up @@ -544,13 +554,15 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
B56C1EB620EA757B00D749F9 /* SiteListMapper.swift in Sources */,
B557DA1A20979D66005962F4 /* Settings.swift in Sources */,
741B950120EBC8A700DD6E2D /* OrderCouponLine.swift in Sources */,
74C8F06420EEB44800B6EDC9 /* OrderNote.swift in Sources */,
B5BB1D0C20A2050300112D92 /* DateFormatter+Woo.swift in Sources */,
B567AF2520A0CCA300AB6C62 /* AuthenticatedRequest.swift in Sources */,
B505F6EA20BEFC3700BB1B69 /* MockupNetwork.swift in Sources */,
B557DA0220975500005962F4 /* JetpackRequest.swift in Sources */,
B56C1EB820EA76F500D749F9 /* Site.swift in Sources */,
B505F6CD20BEE37E00BB1B69 /* AccountMapper.swift in Sources */,
B557DA0D20975DB1005962F4 /* WordPressAPIVersion.swift in Sources */,
B557DA1D20979E7D005962F4 /* Order.swift in Sources */,
Expand Down
29 changes: 29 additions & 0 deletions Networking/Networking/Mapper/SiteListMapper.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import Foundation


/// Mapper: SiteList
///
class SiteListMapper: Mapper {

/// (Attempts) to convert a dictionary into [Site].
///
func map(response: Data) throws -> [Site] {
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .formatted(DateFormatter.Defaults.dateTimeFormatter)

return try decoder.decode(SiteListEnvelope.self, from: response).sites
}
}


/// SiteList Disposable Entity:
/// `Load All Sites` endpoint returns all of its orders within the `sites` key. This entity
/// allows us to do parse all the things with JSONDecoder.
///
private struct SiteListEnvelope: Decodable {
let sites: [Site]

private enum CodingKeys: String, CodingKey {
case sites = "sites"
}
}
60 changes: 60 additions & 0 deletions Networking/Networking/Model/Site.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import Foundation


/// Represents a WordPress.com Site.
///
public struct Site: Decodable {

/// WordPress.com Site Identifier.
///
let siteID: Int

/// Site's Name.
///
let name: String

/// Site's Description.
///
let description: String

/// Site's URL.
///
let url: String

/// Indicates if this site hosts a WordPress Store.
///
let isWordPressStore: Bool


/// Designated Initializer.
///
public init(from decoder: Decoder) throws {
let siteContainer = try decoder.container(keyedBy: SiteKeys.self)

siteID = try siteContainer.decode(Int.self, forKey: .siteID)
name = try siteContainer.decode(String.self, forKey: .name)
description = try siteContainer.decode(String.self, forKey: .description)
url = try siteContainer.decode(String.self, forKey: .url)

let optionsContainer = try siteContainer.nestedContainer(keyedBy: OptionKeys.self, forKey: .options)
isWordPressStore = try optionsContainer.decode(Bool.self, forKey: .isWordPressStore)
}
}


/// Defines all of the Site CodingKeys.
///
private extension Site {

enum SiteKeys: String, CodingKey {
case siteID = "ID"
case name = "name"
case description = "description"
case url = "URL"
case options = "options"
}

enum OptionKeys: String, CodingKey {
case isWordPressStore = "is_wpcom_store"
}
}
15 changes: 15 additions & 0 deletions Networking/Networking/Remote/AccountRemote.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,19 @@ public class AccountRemote: Remote {

enqueue(request, mapper: mapper, completion: completion)
}


/// Loads the Sites collection associated with the WordPress.com User.
///
public func loadSites(completion: @escaping ([Site]?, Error?) -> Void) {
let path = "me/sites"
let parameters = [
"fields": "ID,name,description,URL,options"
]

let request = DotcomRequest(wordpressApiVersion: .mark1_1, method: .get, path: path, parameters: parameters)
let mapper = SiteListMapper()

enqueue(request, mapper: mapper, completion: completion)
}
}
33 changes: 32 additions & 1 deletion Networking/NetworkingTests/Mapper/AccountMapperTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,27 @@ class AccountMapperTests: XCTestCase {
XCTAssertEqual(account.userID, 78972699)
XCTAssertEqual(account.username, "apiexamples")
}

/// Verifies that all of the Site fields are properly parsed.
///
func testSiteFieldsAreProperlyParsed() {
let sites = mapLoadSitesResponse()
XCTAssert(sites?.count == 2)

let first = sites!.first!
XCTAssertEqual(first.siteID, 1112233334444555)
XCTAssertEqual(first.name, "Testing Blog")
XCTAssertEqual(first.description, "Testing Tagline")
XCTAssertEqual(first.url, "https://some-testing-url.testing.blog")
XCTAssertEqual(first.isWordPressStore, true)

let second = sites!.last!
XCTAssertEqual(second.siteID, 11122333344446666)
XCTAssertEqual(second.name, "Thoughts")
XCTAssertEqual(second.description, "Your Favorite Blog")
XCTAssertEqual(second.url, "https://thoughts.testing.blog")
XCTAssertEqual(second.isWordPressStore, false)
}
}


Expand All @@ -28,7 +49,7 @@ class AccountMapperTests: XCTestCase {
//
private extension AccountMapperTests {

/// Returns the AccountMapper output upon receiving `me` (Data Encoded)
/// Returns the AccountMapper output upon receiving `me` mockup response (Data Encoded).
///
func mapLoadAccountResponse() -> Account? {
guard let response = Loader.contentsOf("me") else {
Expand All @@ -37,4 +58,14 @@ private extension AccountMapperTests {

return try? AccountMapper().map(response: response)
}

/// Returns the SiteListMapper output upon receiving `me/sites` mockup response (Data Encoded).
///
func mapLoadSitesResponse() -> [Site]? {
guard let response = Loader.contentsOf("sites") else {
return nil
}

return try? SiteListMapper().map(response: response)
}
}
Loading