diff --git a/Networking/Networking.xcodeproj/project.pbxproj b/Networking/Networking.xcodeproj/project.pbxproj index b7521e631a0..a491dcbffbd 100644 --- a/Networking/Networking.xcodeproj/project.pbxproj +++ b/Networking/Networking.xcodeproj/project.pbxproj @@ -10,6 +10,13 @@ 21DB5B99C4107CF69C0A57EC /* Pods_NetworkingTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 69314EDE650855CAF927057E /* Pods_NetworkingTests.framework */; }; 6647C0161DAC6AB6570C53A7 /* Pods_Networking.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F3F25DC15EC1D7C631169CB5 /* Pods_Networking.framework */; }; 741B950120EBC8A700DD6E2D /* OrderCouponLine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 741B950020EBC8A700DD6E2D /* OrderCouponLine.swift */; }; + 74C8F06420EEB44800B6EDC9 /* OrderNote.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74C8F06320EEB44800B6EDC9 /* OrderNote.swift */; }; + 74C8F06620EEB76400B6EDC9 /* order-notes.json in Resources */ = {isa = PBXBuildFile; fileRef = 74C8F06520EEB76400B6EDC9 /* order-notes.json */; }; + 74C8F06820EEB7BD00B6EDC9 /* OrderNotesMapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74C8F06720EEB7BC00B6EDC9 /* OrderNotesMapper.swift */; }; + 74C8F06A20EEBC8C00B6EDC9 /* OrderMapperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74C8F06920EEBC8C00B6EDC9 /* OrderMapperTests.swift */; }; + 74C8F06C20EEBD5D00B6EDC9 /* broken-order.json in Resources */ = {isa = PBXBuildFile; fileRef = 74C8F06B20EEBD5D00B6EDC9 /* broken-order.json */; }; + 74C8F06E20EEC1E800B6EDC9 /* OrderNotesMapperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74C8F06D20EEC1E700B6EDC9 /* OrderNotesMapperTests.swift */; }; + 74C8F07020EEC3A800B6EDC9 /* broken-notes.json in Resources */ = {isa = PBXBuildFile; fileRef = 74C8F06F20EEC3A800B6EDC9 /* broken-notes.json */; }; B505F6CD20BEE37E00BB1B69 /* AccountMapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = B505F6CC20BEE37E00BB1B69 /* AccountMapper.swift */; }; B505F6CF20BEE38B00BB1B69 /* Account.swift in Sources */ = {isa = PBXBuildFile; fileRef = B505F6CE20BEE38B00BB1B69 /* Account.swift */; }; B505F6D120BEE39600BB1B69 /* AccountRemote.swift in Sources */ = {isa = PBXBuildFile; fileRef = B505F6D020BEE39600BB1B69 /* AccountRemote.swift */; }; @@ -49,7 +56,7 @@ B5C6FCCF20A3592900A4F8E4 /* OrderItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5C6FCCE20A3592900A4F8E4 /* OrderItem.swift */; }; B5C6FCD420A373BB00A4F8E4 /* OrderMapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5C6FCD320A373BA00A4F8E4 /* OrderMapper.swift */; }; B5C6FCD620A3768900A4F8E4 /* order.json in Resources */ = {isa = PBXBuildFile; fileRef = B5C6FCD520A3768900A4F8E4 /* order.json */; }; - CE20179320E3EFA7005B4C18 /* broken-order.json in Resources */ = {isa = PBXBuildFile; fileRef = CE20179220E3EFA7005B4C18 /* broken-order.json */; }; + CE20179320E3EFA7005B4C18 /* broken-orders.json in Resources */ = {isa = PBXBuildFile; fileRef = CE20179220E3EFA7005B4C18 /* broken-orders.json */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -65,6 +72,13 @@ /* Begin PBXFileReference section */ 69314EDE650855CAF927057E /* Pods_NetworkingTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_NetworkingTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 741B950020EBC8A700DD6E2D /* OrderCouponLine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OrderCouponLine.swift; sourceTree = ""; }; + 74C8F06320EEB44800B6EDC9 /* OrderNote.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OrderNote.swift; sourceTree = ""; }; + 74C8F06520EEB76400B6EDC9 /* order-notes.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "order-notes.json"; sourceTree = ""; }; + 74C8F06720EEB7BC00B6EDC9 /* OrderNotesMapper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OrderNotesMapper.swift; sourceTree = ""; }; + 74C8F06920EEBC8C00B6EDC9 /* OrderMapperTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OrderMapperTests.swift; sourceTree = ""; }; + 74C8F06B20EEBD5D00B6EDC9 /* broken-order.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "broken-order.json"; sourceTree = ""; }; + 74C8F06D20EEC1E700B6EDC9 /* OrderNotesMapperTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OrderNotesMapperTests.swift; sourceTree = ""; }; + 74C8F06F20EEC3A800B6EDC9 /* broken-notes.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "broken-notes.json"; sourceTree = ""; }; 753D6504FF01F09F6A33B73E /* Pods-Networking.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Networking.debug.xcconfig"; path = "../Pods/Target Support Files/Pods-Networking/Pods-Networking.debug.xcconfig"; sourceTree = ""; }; B505F6CC20BEE37E00BB1B69 /* AccountMapper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountMapper.swift; sourceTree = ""; }; B505F6CE20BEE38B00BB1B69 /* Account.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Account.swift; sourceTree = ""; }; @@ -110,7 +124,7 @@ B5C6FCD520A3768900A4F8E4 /* order.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = order.json; sourceTree = ""; }; BD9439D9B8F2C1ED2EADAA51 /* Pods-NetworkingTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NetworkingTests.debug.xcconfig"; path = "../Pods/Target Support Files/Pods-NetworkingTests/Pods-NetworkingTests.debug.xcconfig"; sourceTree = ""; }; C8F9A8CC6F90A8C9B5EF2EE2 /* Pods-Networking.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Networking.release.xcconfig"; path = "../Pods/Target Support Files/Pods-Networking/Pods-Networking.release.xcconfig"; sourceTree = ""; }; - CE20179220E3EFA7005B4C18 /* broken-order.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "broken-order.json"; sourceTree = ""; }; + CE20179220E3EFA7005B4C18 /* broken-orders.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "broken-orders.json"; sourceTree = ""; }; F3F25DC15EC1D7C631169CB5 /* Pods_Networking.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Networking.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 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 = ""; }; /* End PBXFileReference section */ @@ -279,10 +293,11 @@ children = ( B505F6CE20BEE38B00BB1B69 /* Account.swift */, B5BB1D0F20A237FB00112D92 /* Address.swift */, - 741B950020EBC8A700DD6E2D /* OrderCouponLine.swift */, B557DA1C20979E7D005962F4 /* Order.swift */, - B5BB1D1120A255EC00112D92 /* OrderStatus.swift */, + 741B950020EBC8A700DD6E2D /* OrderCouponLine.swift */, B5C6FCCE20A3592900A4F8E4 /* OrderItem.swift */, + 74C8F06320EEB44800B6EDC9 /* OrderNote.swift */, + B5BB1D1120A255EC00112D92 /* OrderStatus.swift */, ); path = Model; sourceTree = ""; @@ -293,7 +308,10 @@ B505F6D420BEE4E600BB1B69 /* me.json */, B559EBA920A0B5CD00836CD4 /* orders-load-all.json */, B5C6FCD520A3768900A4F8E4 /* order.json */, - CE20179220E3EFA7005B4C18 /* broken-order.json */, + CE20179220E3EFA7005B4C18 /* broken-orders.json */, + 74C8F06B20EEBD5D00B6EDC9 /* broken-order.json */, + 74C8F06520EEB76400B6EDC9 /* order-notes.json */, + 74C8F06F20EEC3A800B6EDC9 /* broken-notes.json */, ); path = Responses; sourceTree = ""; @@ -305,6 +323,7 @@ B505F6CC20BEE37E00BB1B69 /* AccountMapper.swift */, B5C6FCD320A373BA00A4F8E4 /* OrderMapper.swift */, B567AF2A20A0FA4200AB6C62 /* OrderListMapper.swift */, + 74C8F06720EEB7BC00B6EDC9 /* OrderNotesMapper.swift */, ); path = Mapper; sourceTree = ""; @@ -330,6 +349,8 @@ children = ( B505F6D220BEE3A500BB1B69 /* AccountMapperTests.swift */, B5C6FCCC20A34B8300A4F8E4 /* OrderListMapperTests.swift */, + 74C8F06920EEBC8C00B6EDC9 /* OrderMapperTests.swift */, + 74C8F06D20EEC1E700B6EDC9 /* OrderNotesMapperTests.swift */, ); path = Mapper; sourceTree = ""; @@ -447,10 +468,13 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 74C8F06620EEB76400B6EDC9 /* order-notes.json in Resources */, + 74C8F06C20EEBD5D00B6EDC9 /* broken-order.json in Resources */, B505F6D520BEE4E700BB1B69 /* me.json in Resources */, B5C6FCD620A3768900A4F8E4 /* order.json in Resources */, B559EBAA20A0B5CD00836CD4 /* orders-load-all.json in Resources */, - CE20179320E3EFA7005B4C18 /* broken-order.json in Resources */, + CE20179320E3EFA7005B4C18 /* broken-orders.json in Resources */, + 74C8F07020EEC3A800B6EDC9 /* broken-notes.json in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -522,6 +546,7 @@ files = ( 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 */, @@ -537,6 +562,7 @@ B5C6FCCF20A3592900A4F8E4 /* OrderItem.swift in Sources */, B505F6EC20BEFDC200BB1B69 /* Loader.swift in Sources */, B5BB1D1220A255EC00112D92 /* OrderStatus.swift in Sources */, + 74C8F06820EEB7BD00B6EDC9 /* OrderNotesMapper.swift in Sources */, B5BB1D1020A237FB00112D92 /* Address.swift in Sources */, B557DA0420975500005962F4 /* OrdersRemote.swift in Sources */, B5C6FCD420A373BB00A4F8E4 /* OrderMapper.swift in Sources */, @@ -554,10 +580,12 @@ files = ( B505F6D320BEE3A500BB1B69 /* AccountMapperTests.swift in Sources */, B5C6FCC820A32E4800A4F8E4 /* DateFormatterWooTests.swift in Sources */, + 74C8F06A20EEBC8C00B6EDC9 /* OrderMapperTests.swift in Sources */, B567AF3120A0FB8F00AB6C62 /* JetpackRequestTests.swift in Sources */, B505F6D720BEE58800BB1B69 /* AccountRemoteTests.swift in Sources */, B518662A20A09C6F00037A38 /* OrdersRemoteTests.swift in Sources */, B5969E1520A47F99005E9DF1 /* RemoteTests.swift in Sources */, + 74C8F06E20EEC1E800B6EDC9 /* OrderNotesMapperTests.swift in Sources */, B567AF2F20A0FB8F00AB6C62 /* AuthenticatedRequestTests.swift in Sources */, B5C6FCCD20A34B8300A4F8E4 /* OrderListMapperTests.swift in Sources */, B518663520A0A2E800037A38 /* Constants.swift in Sources */, diff --git a/Networking/Networking/Mapper/OrderNotesMapper.swift b/Networking/Networking/Mapper/OrderNotesMapper.swift new file mode 100644 index 00000000000..8fc00ef0396 --- /dev/null +++ b/Networking/Networking/Mapper/OrderNotesMapper.swift @@ -0,0 +1,29 @@ +import Foundation + + +/// Mapper: OrderNotes +/// +class OrderNotesMapper: Mapper { + + /// (Attempts) to convert a dictionary into [OrderNote]. + /// + func map(response: Data) throws -> [OrderNote] { + let decoder = JSONDecoder() + decoder.dateDecodingStrategy = .formatted(DateFormatter.Defaults.dateTimeFormatter) + + return try decoder.decode(OrderNotesEnvelope.self, from: response).orderNotes + } +} + + +/// OrderNote Disposable Entity: +/// `Load Order Notes` endpoint returns all of its notes within the `data` key. This entity +/// allows us to do parse all the things with JSONDecoder. +/// +private struct OrderNotesEnvelope: Decodable { + let orderNotes: [OrderNote] + + private enum CodingKeys: String, CodingKey { + case orderNotes = "data" + } +} diff --git a/Networking/Networking/Model/OrderNote.swift b/Networking/Networking/Model/OrderNote.swift new file mode 100644 index 00000000000..9594dbca06e --- /dev/null +++ b/Networking/Networking/Model/OrderNote.swift @@ -0,0 +1,63 @@ +import Foundation + + +/// Represents an Order's Note Entity. +/// +public struct OrderNote: Decodable { + public let noteId: Int + public let dateCreated: Date + public let note: String + public let isCustomerNote: Bool + + /// Order struct initializer. + /// + init(noteId: Int, dateCreated: Date, note: String, isCustomerNote: Bool) { + self.noteId = noteId + self.dateCreated = dateCreated + self.note = note + self.isCustomerNote = isCustomerNote + } + + /// The public initializer for OrderNote. + /// + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + let noteId = try container.decode(Int.self, forKey: .noteId) + let dateCreated = try container.decodeIfPresent(Date.self, forKey: .dateCreated) ?? Date() + let note = try container.decode(String.self, forKey: .note) + let isCustomerNote = try container.decode(Bool.self, forKey: .isCustomerNote) + + self.init(noteId: noteId, dateCreated: dateCreated, note: note, isCustomerNote: isCustomerNote) // initialize the struct + } +} + + +/// Defines all of the OrderNote's CodingKeys. +/// +private extension OrderNote { + + enum CodingKeys: String, CodingKey { + case noteId = "id" + case dateCreated = "date_created_gmt" + case note = "note" + case isCustomerNote = "customer_note" + } +} + + +// MARK: - Comparable Conformance +// +extension OrderNote: Comparable { + public static func == (lhs: OrderNote, rhs: OrderNote) -> Bool { + return lhs.noteId == rhs.noteId && + lhs.dateCreated == rhs.dateCreated && + lhs.note == rhs.note && + lhs.isCustomerNote == rhs.isCustomerNote + } + + public static func < (lhs: OrderNote, rhs: OrderNote) -> Bool { + return lhs.noteId < rhs.noteId || + (lhs.noteId == rhs.noteId && lhs.dateCreated < rhs.dateCreated) || + (lhs.noteId == rhs.noteId && lhs.dateCreated == rhs.dateCreated && lhs.note < rhs.note) + } +} diff --git a/Networking/Networking/Remote/OrdersRemote.swift b/Networking/Networking/Remote/OrdersRemote.swift index 43363f48504..9328b993eab 100644 --- a/Networking/Networking/Remote/OrdersRemote.swift +++ b/Networking/Networking/Remote/OrdersRemote.swift @@ -25,8 +25,8 @@ public class OrdersRemote: Remote { /// Retrieves a specific `Order` /// /// - Parameters: - /// - siteID: Site for which we'll fetch remote orders. - /// - orderID: Order for which we'll fetch remote orders. + /// - siteID: Site which hosts the Order. + /// - orderID: Identifier of the Order. /// - completion: Closure to be executed upon completion. /// public func loadOrder(for siteID: Int, orderID: Int, completion: @escaping (Order?, Error?) -> Void) { @@ -37,6 +37,21 @@ public class OrdersRemote: Remote { enqueue(request, mapper: mapper, completion: completion) } + /// Retrieves the notes for a specific `Order` + /// + /// - Parameters: + /// - siteID: Site which hosts the Order. + /// - orderID: Identifier of the Order. + /// - completion: Closure to be executed upon completion. + /// + public func loadOrderNotes(for siteID: Int, orderID: Int, completion: @escaping ([OrderNote]?, Error?) -> Void) { + let path = "\(Constants.ordersPath)/\(orderID)/\(Constants.notesPath)/" + let request = JetpackRequest(wooApiVersion: .mark2, method: .get, siteID: siteID, path: path, parameters: nil) + let mapper = OrderNotesMapper() + + enqueue(request, mapper: mapper, completion: completion) + } + /// Updates the `OrderStatus` of a given Order. /// /// - Parameters: @@ -60,8 +75,9 @@ public class OrdersRemote: Remote { // private extension OrdersRemote { enum Constants { - static let defaultPageSize: Int = 75 - static let ordersPath: String = "orders" + static let defaultPageSize: Int = 75 + static let ordersPath: String = "orders" + static let notesPath: String = "notes" } enum ParameterKeys { diff --git a/Networking/NetworkingTests/Mapper/OrderListMapperTests.swift b/Networking/NetworkingTests/Mapper/OrderListMapperTests.swift index 39cc735389d..e86894d0580 100644 --- a/Networking/NetworkingTests/Mapper/OrderListMapperTests.swift +++ b/Networking/NetworkingTests/Mapper/OrderListMapperTests.swift @@ -86,7 +86,7 @@ class OrderListMapperTests: XCTestCase { let brokenOrder = orders[0] let format = DateFormatter() format.dateStyle = .short - + let orderCreatedString = format.string(from: brokenOrder.dateCreated) let todayCreatedString = format.string(from: Date()) XCTAssertEqual(orderCreatedString, todayCreatedString) @@ -120,6 +120,6 @@ private extension OrderListMapperTests { /// Returns the OrderlistMapper output upon receiving `broken-order` /// func mapLoadBrokenOrderResponse() -> [Order] { - return mapOrders(from: "broken-order") + return mapOrders(from: "broken-orders") } } diff --git a/Networking/NetworkingTests/Mapper/OrderMapperTests.swift b/Networking/NetworkingTests/Mapper/OrderMapperTests.swift new file mode 100644 index 00000000000..03ecc63981f --- /dev/null +++ b/Networking/NetworkingTests/Mapper/OrderMapperTests.swift @@ -0,0 +1,130 @@ +import XCTest +@testable import Networking + + +/// OrderMapper Unit Tests +/// +class OrderMapperTests: XCTestCase { + + /// Verifies that all of the Order Fields are parsed correctly. + /// + func testOrderFieldsAreProperlyParsed() { + guard let order = mapLoadOrderResponse() else { + XCTFail() + return + } + + let dateCreated = DateFormatter.Defaults.dateTimeFormatter.date(from: "2018-01-24T16:21:48") + let dateModified = DateFormatter.Defaults.dateTimeFormatter.date(from: "2018-05-09T18:15:30") + let datePaid = DateFormatter.Defaults.dateTimeFormatter.date(from: "2018-05-03T19:24:55") + + XCTAssertEqual(order.orderID, 1467) + XCTAssertEqual(order.parentID, 0) + XCTAssertEqual(order.customerID, 100) + XCTAssertEqual(order.number, "1467") + XCTAssert(order.status == .processing) + XCTAssertEqual(order.currency, "USD") + XCTAssertEqual(order.customerNote, "") + XCTAssertEqual(order.dateCreated, dateCreated) + XCTAssertEqual(order.dateModified, dateModified) + XCTAssertEqual(order.datePaid, datePaid) + XCTAssertEqual(order.discountTotal, "0.00") + XCTAssertEqual(order.discountTax, "0.00") + XCTAssertEqual(order.shippingTotal, "0.00") + XCTAssertEqual(order.shippingTax, "0.00") + XCTAssertEqual(order.total, "102.00") + XCTAssertEqual(order.totalTax, "2.00") + } + + /// Verifies that all of the Order Address fields are parsed correctly. + /// + func testOrderAddressesAreCorrectlyParsed() { + guard let order = mapLoadOrderResponse() else { + XCTFail() + return + } + + let dummyAddresses = [order.billingAddress, order.shippingAddress] + + for address in dummyAddresses { + XCTAssertEqual(address.firstName, "Maria") + XCTAssertEqual(address.lastName, "Scrambled") + XCTAssertEqual(address.company, "Logged Out") + XCTAssertEqual(address.address1, "9999 Scrambled") + XCTAssertEqual(address.address2, "") + XCTAssertEqual(address.city, "Omaha") + XCTAssertEqual(address.state, "NE") + XCTAssertEqual(address.postcode, "68124") + XCTAssertEqual(address.country, "US") + } + } + + /// Verifies that all of the Order Items are parsed correctly. + /// + func testOrderItemsAreCorrectlyParsed() { + guard let order = mapLoadOrderResponse() else { + XCTFail() + return + } + + let firstItem = order.items[0] + XCTAssertEqual(firstItem.itemID, 3) + XCTAssertEqual(firstItem.name, "ARC Reactor") + XCTAssertEqual(firstItem.productID, 1450) + XCTAssertEqual(firstItem.quantity, 1) + XCTAssertEqual(firstItem.sku, "100") + XCTAssertEqual(firstItem.subtotal, "100.00") + XCTAssertEqual(firstItem.subtotalTax, "2.00") + XCTAssertEqual(firstItem.taxClass, "") + XCTAssertEqual(firstItem.total, "100.00") + XCTAssertEqual(firstItem.totalTax, "2.00") + XCTAssertEqual(firstItem.variationID, 0) + } + + /// Verifies that an Order in a broken state does [gets default values] | [gets skipped while parsing] + /// + func testOrderHasDefaultDateCreatedWhenNullDateReceived() { + guard let brokenOrder = mapLoadBrokenOrderResponse() else { + XCTFail() + return + } + + let format = DateFormatter() + format.dateStyle = .short + + let orderCreatedString = format.string(from: brokenOrder.dateCreated) + let todayCreatedString = format.string(from: Date()) + XCTAssertEqual(orderCreatedString, todayCreatedString) + + let orderModifiedString = format.string(from: brokenOrder.dateModified) + XCTAssertEqual(orderModifiedString, todayCreatedString) + } +} + + +/// Private Methods. +/// +private extension OrderMapperTests { + + /// Returns the OrderMapper output upon receiving `filename` (Data Encoded) + /// + func mapOrder(from filename: String) -> Order? { + guard let response = Loader.contentsOf(filename) else { + return nil + } + + return try! OrderMapper().map(response: response) + } + + /// Returns the OrderMapper output upon receiving `order` + /// + func mapLoadOrderResponse() -> Order? { + return mapOrder(from: "order") + } + + /// Returns the OrderMapper output upon receiving `broken-order` + /// + func mapLoadBrokenOrderResponse() -> Order? { + return mapOrder(from: "broken-order") + } +} diff --git a/Networking/NetworkingTests/Mapper/OrderNotesMapperTests.swift b/Networking/NetworkingTests/Mapper/OrderNotesMapperTests.swift new file mode 100644 index 00000000000..6e5fbb8f788 --- /dev/null +++ b/Networking/NetworkingTests/Mapper/OrderNotesMapperTests.swift @@ -0,0 +1,66 @@ +import XCTest +@testable import Networking + + +/// OrderListMapper Unit Tests +/// +class OrderNotesMapperTests: XCTestCase { + + /// Verifies that all of the OrderNote Fields are parsed correctly. + /// + func testNoteFieldsAreProperlyParsed() { + let notes = mapLoadAllOrderNotesResponse() + XCTAssertEqual(notes.count, 18) + + let firstNote = notes[0] + let dateCreated = DateFormatter.Defaults.dateTimeFormatter.date(from: "2018-06-23T17:06:55") + + XCTAssertEqual(firstNote.noteId, 2261) + XCTAssertEqual(firstNote.dateCreated, dateCreated) + XCTAssertEqual(firstNote.note, "I love your products!") + XCTAssertEqual(firstNote.isCustomerNote, true) + } + + /// Verifies that an Note in a broken state does [gets default values] | [gets skipped while parsing] + /// + func testNoteHasDefaultDateCreatedWhenNullDateReceived() { + let notes = mapLoadBrokenOrderNotesResponse() + XCTAssert(notes.count == 1) + + let brokenNote = notes[0] + let format = DateFormatter() + format.dateStyle = .short + + let orderCreatedString = format.string(from: brokenNote.dateCreated) + let todayCreatedString = format.string(from: Date()) + XCTAssertEqual(orderCreatedString, todayCreatedString) + } +} + + +/// Private Methods. +/// +private extension OrderNotesMapperTests { + + /// Returns the OrderNotesMapper output upon receiving `filename` (Data Encoded) + /// + func mapNotes(from filename: String) -> [OrderNote] { + guard let response = Loader.contentsOf(filename) else { + return [] + } + + return try! OrderNotesMapper().map(response: response) + } + + /// Returns the OrderNotesMapper output upon receiving `orders-load-all` + /// + func mapLoadAllOrderNotesResponse() -> [OrderNote] { + return mapNotes(from: "order-notes") + } + + /// Returns the OrderNotesMapper output upon receiving `broken-order` + /// + func mapLoadBrokenOrderNotesResponse() -> [OrderNote] { + return mapNotes(from: "broken-notes") + } +} diff --git a/Networking/NetworkingTests/Remote/OrdersRemoteTests.swift b/Networking/NetworkingTests/Remote/OrdersRemoteTests.swift index a849ca47241..b77c64094a4 100644 --- a/Networking/NetworkingTests/Remote/OrdersRemoteTests.swift +++ b/Networking/NetworkingTests/Remote/OrdersRemoteTests.swift @@ -26,6 +26,8 @@ class OrdersRemoteTests: XCTestCase { } + // MARK: - Load All Orders Tests + /// Verifies that loadAllOrders properly parses the `orders-load-all` sample response. /// func testLoadAllOrdersProperlyReturnsParsedOrders() { @@ -44,6 +46,24 @@ class OrdersRemoteTests: XCTestCase { wait(for: [expectation], timeout: Constants.expectationTimeout) } + /// Verifies that loadAllOrders properly relays Networking Layer errors. + /// + func testLoadAllOrdersProperlyRelaysNetwokingErrors() { + let remote = OrdersRemote(network: network) + let expectation = self.expectation(description: "Load All Orders") + + remote.loadAllOrders(for: sampleSiteID) { orders, error in + XCTAssertNil(orders) + XCTAssertNotNil(error) + expectation.fulfill() + } + + wait(for: [expectation], timeout: Constants.expectationTimeout) + } + + + // MARK: - Load Order Tests + /// Verifies that loadOrder properly parses the `order` sample response. /// func testLoadSingleOrderProperlyReturnsParsedOrder() { @@ -61,14 +81,14 @@ class OrdersRemoteTests: XCTestCase { wait(for: [expectation], timeout: Constants.expectationTimeout) } - /// Verifies that loadAllOrders properly relays Networking Layer errors. + /// Verifies that loadOrder properly relays any Networking Layer errors. /// - func testLoadAllOrdersProperlyRelaysNetwokingErrors() { + func testLoadSingleOrderProperlyRelaysNetwokingErrors() { let remote = OrdersRemote(network: network) - let expectation = self.expectation(description: "Load All Orders") + let expectation = self.expectation(description: "Update Order") - remote.loadAllOrders(for: sampleSiteID) { orders, error in - XCTAssertNil(orders) + remote.loadOrder(for: sampleSiteID, orderID: sampleOrderID) { order, error in + XCTAssertNil(order) XCTAssertNotNil(error) expectation.fulfill() } @@ -76,6 +96,9 @@ class OrdersRemoteTests: XCTestCase { wait(for: [expectation], timeout: Constants.expectationTimeout) } + + // MARK: - Update Orders Tests + /// Verifies that updateOrder properly parses the `order` sample response. /// func testUpdateOrderProperlyReturnsParsedOrder() { @@ -107,4 +130,40 @@ class OrdersRemoteTests: XCTestCase { wait(for: [expectation], timeout: Constants.expectationTimeout) } + + + // MARK: - Load Order Notes Tests + + /// Verifies that loadOrderNotes properly parses the `order-notes` sample response. + /// + func testLoadOrderNotesProperlyReturnsParsedNotes() { + let remote = OrdersRemote(network: network) + let expectation = self.expectation(description: "Load Order Notes") + + network.simulateResponse(requestUrlSuffix: "orders/\(sampleOrderID)/notes/", filename: "order-notes") + + remote.loadOrderNotes(for: sampleSiteID, orderID: sampleOrderID) { orderNotes, error in + XCTAssertNil(error) + XCTAssertNotNil(orderNotes) + XCTAssertEqual(orderNotes?.count, 18) + expectation.fulfill() + } + + wait(for: [expectation], timeout: Constants.expectationTimeout) + } + + /// Verifies that loadOrderNotes properly relays any Networking Layer errors. + /// + func testLoadOrderNotesProperlyRelaysNetwokingErrors() { + let remote = OrdersRemote(network: network) + let expectation = self.expectation(description: "Load Order Notes") + + remote.loadOrderNotes(for: sampleSiteID, orderID: sampleOrderID) { orderNotes, error in + XCTAssertNil(orderNotes) + XCTAssertNotNil(error) + expectation.fulfill() + } + + wait(for: [expectation], timeout: Constants.expectationTimeout) + } } diff --git a/Networking/NetworkingTests/Responses/broken-notes.json b/Networking/NetworkingTests/Responses/broken-notes.json new file mode 100644 index 00000000000..144e54f099e --- /dev/null +++ b/Networking/NetworkingTests/Responses/broken-notes.json @@ -0,0 +1,10 @@ +{ + "data": [{ + "id": 2261, + "date_created": null, + "date_created_gmt": null, + "note": "", + "customer_note": false, + "_links": null + }] +} diff --git a/Networking/NetworkingTests/Responses/broken-order.json b/Networking/NetworkingTests/Responses/broken-order.json index 2914a415c2a..8ef03b798da 100644 --- a/Networking/NetworkingTests/Responses/broken-order.json +++ b/Networking/NetworkingTests/Responses/broken-order.json @@ -1,5 +1,5 @@ { - "data": [{ + "data": { "id": 85, "parent_id": 0, "number": "85", @@ -91,5 +91,5 @@ "self": [], "collection": [] } - }] + } } diff --git a/Networking/NetworkingTests/Responses/broken-orders.json b/Networking/NetworkingTests/Responses/broken-orders.json new file mode 100644 index 00000000000..2914a415c2a --- /dev/null +++ b/Networking/NetworkingTests/Responses/broken-orders.json @@ -0,0 +1,95 @@ +{ + "data": [{ + "id": 85, + "parent_id": 0, + "number": "85", + "order_key": false, + "created_via": "", + "version": "", + "status": "draft", + "currency": "GBP", + "date_created": null, + "date_created_gmt": null, + "date_modified": null, + "date_modified_gmt": null, + "discount_total": "0.00", + "discount_tax": "0.00", + "shipping_total": "0.00", + "shipping_tax": "0.00", + "cart_tax": "0.00", + "total": "0.00", + "total_tax": "0.00", + "prices_include_tax": false, + "customer_id": 0, + "customer_ip_address": "", + "customer_user_agent": "", + "customer_note": "", + "billing": { + "first_name": "", + "last_name": "", + "company": "", + "address_1": "", + "address_2": "", + "city": "", + "state": "", + "postcode": "", + "country": "", + "email": "", + "phone": "" + }, + "shipping": { + "first_name": "", + "last_name": "", + "company": "", + "address_1": "", + "address_2": "", + "city": "", + "state": "", + "postcode": "", + "country": "" + }, + "payment_method": "", + "payment_method_title": "", + "transaction_id": "", + "date_paid": null, + "date_paid_gmt": null, + "date_completed": null, + "date_completed_gmt": null, + "cart_hash": "", + "meta_data": [], + "line_items": [], + "tax_lines": [], + "shipping_lines": [{ + "id": 1, + "method_title": "Shipping", + "method_id": "", + "total": "0.00", + "total_tax": "0.00", + "taxes": [], + "meta_data": [] + }, { + "id": 2, + "method_title": "Shipping", + "method_id": "", + "total": "0.00", + "total_tax": "0.00", + "taxes": [], + "meta_data": [] + }, { + "id": 3, + "method_title": "Shipping", + "method_id": "", + "total": "0.00", + "total_tax": "0.00", + "taxes": [], + "meta_data": [] + }], + "fee_lines": [], + "coupon_lines": [], + "refunds": [], + "_links": { + "self": [], + "collection": [] + } + }] +} diff --git a/Networking/NetworkingTests/Responses/order-notes.json b/Networking/NetworkingTests/Responses/order-notes.json new file mode 100644 index 00000000000..721c42cef39 --- /dev/null +++ b/Networking/NetworkingTests/Responses/order-notes.json @@ -0,0 +1,436 @@ +{ + "data": [ + { + "id": 2261, + "date_created": "2018-06-23T13:06:55", + "date_created_gmt": "2018-06-23T17:06:55", + "note": "I love your products!", + "customer_note": true, + "_links": { + "self": [ + { + "href": "https:\/\/jamosova3.mystagingwebsite.com\/wp-json\/wc\/v2\/orders\/1044\/notes\/2261" + } + ], + "collection": [ + { + "href": "https:\/\/jamosova3.mystagingwebsite.com\/wp-json\/wc\/v2\/orders\/1044\/notes" + } + ], + "up": [ + { + "href": "https:\/\/jamosova3.mystagingwebsite.com\/wp-json\/wc\/v2\/orders\/1044" + } + ] + } + }, + { + "id": 2099, + "date_created": "2018-05-28T23:07:46", + "date_created_gmt": "2018-05-29T03:07:46", + "note": "Order status changed from Completed to Processing.", + "customer_note": false, + "_links": { + "self": [ + { + "href": "https:\/\/jamosova3.mystagingwebsite.com\/wp-json\/wc\/v2\/orders\/1044\/notes\/2099" + } + ], + "collection": [ + { + "href": "https:\/\/jamosova3.mystagingwebsite.com\/wp-json\/wc\/v2\/orders\/1044\/notes" + } + ], + "up": [ + { + "href": "https:\/\/jamosova3.mystagingwebsite.com\/wp-json\/wc\/v2\/orders\/1044" + } + ] + } + }, + { + "id": 2073, + "date_created": "2018-05-26T01:00:24", + "date_created_gmt": "2018-05-26T05:00:24", + "note": "Order status changed from Processing to Completed.", + "customer_note": false, + "_links": { + "self": [ + { + "href": "https:\/\/jamosova3.mystagingwebsite.com\/wp-json\/wc\/v2\/orders\/1044\/notes\/2073" + } + ], + "collection": [ + { + "href": "https:\/\/jamosova3.mystagingwebsite.com\/wp-json\/wc\/v2\/orders\/1044\/notes" + } + ], + "up": [ + { + "href": "https:\/\/jamosova3.mystagingwebsite.com\/wp-json\/wc\/v2\/orders\/1044" + } + ] + } + }, + { + "id": 2072, + "date_created": "2018-05-26T00:59:48", + "date_created_gmt": "2018-05-26T04:59:48", + "note": "Order status changed from Completed to Processing.", + "customer_note": false, + "_links": { + "self": [ + { + "href": "https:\/\/jamosova3.mystagingwebsite.com\/wp-json\/wc\/v2\/orders\/1044\/notes\/2072" + } + ], + "collection": [ + { + "href": "https:\/\/jamosova3.mystagingwebsite.com\/wp-json\/wc\/v2\/orders\/1044\/notes" + } + ], + "up": [ + { + "href": "https:\/\/jamosova3.mystagingwebsite.com\/wp-json\/wc\/v2\/orders\/1044" + } + ] + } + }, + { + "id": 2071, + "date_created": "2018-05-26T00:59:44", + "date_created_gmt": "2018-05-26T04:59:44", + "note": "Order status changed from Processing to Completed.", + "customer_note": false, + "_links": { + "self": [ + { + "href": "https:\/\/jamosova3.mystagingwebsite.com\/wp-json\/wc\/v2\/orders\/1044\/notes\/2071" + } + ], + "collection": [ + { + "href": "https:\/\/jamosova3.mystagingwebsite.com\/wp-json\/wc\/v2\/orders\/1044\/notes" + } + ], + "up": [ + { + "href": "https:\/\/jamosova3.mystagingwebsite.com\/wp-json\/wc\/v2\/orders\/1044" + } + ] + } + }, + { + "id": 2055, + "date_created": "2018-05-26T00:39:42", + "date_created_gmt": "2018-05-26T04:39:42", + "note": "Order status changed from Completed to Processing.", + "customer_note": false, + "_links": { + "self": [ + { + "href": "https:\/\/jamosova3.mystagingwebsite.com\/wp-json\/wc\/v2\/orders\/1044\/notes\/2055" + } + ], + "collection": [ + { + "href": "https:\/\/jamosova3.mystagingwebsite.com\/wp-json\/wc\/v2\/orders\/1044\/notes" + } + ], + "up": [ + { + "href": "https:\/\/jamosova3.mystagingwebsite.com\/wp-json\/wc\/v2\/orders\/1044" + } + ] + } + }, + { + "id": 2054, + "date_created": "2018-05-26T00:39:39", + "date_created_gmt": "2018-05-26T04:39:39", + "note": "Order status changed from Processing to Completed.", + "customer_note": false, + "_links": { + "self": [ + { + "href": "https:\/\/jamosova3.mystagingwebsite.com\/wp-json\/wc\/v2\/orders\/1044\/notes\/2054" + } + ], + "collection": [ + { + "href": "https:\/\/jamosova3.mystagingwebsite.com\/wp-json\/wc\/v2\/orders\/1044\/notes" + } + ], + "up": [ + { + "href": "https:\/\/jamosova3.mystagingwebsite.com\/wp-json\/wc\/v2\/orders\/1044" + } + ] + } + }, + { + "id": 2043, + "date_created": "2018-05-26T00:02:55", + "date_created_gmt": "2018-05-26T04:02:55", + "note": "Order status changed from Completed to Processing.", + "customer_note": false, + "_links": { + "self": [ + { + "href": "https:\/\/jamosova3.mystagingwebsite.com\/wp-json\/wc\/v2\/orders\/1044\/notes\/2043" + } + ], + "collection": [ + { + "href": "https:\/\/jamosova3.mystagingwebsite.com\/wp-json\/wc\/v2\/orders\/1044\/notes" + } + ], + "up": [ + { + "href": "https:\/\/jamosova3.mystagingwebsite.com\/wp-json\/wc\/v2\/orders\/1044" + } + ] + } + }, + { + "id": 2037, + "date_created": "2018-05-25T22:03:19", + "date_created_gmt": "2018-05-26T02:03:19", + "note": "Order status changed from Processing to Completed.", + "customer_note": false, + "_links": { + "self": [ + { + "href": "https:\/\/jamosova3.mystagingwebsite.com\/wp-json\/wc\/v2\/orders\/1044\/notes\/2037" + } + ], + "collection": [ + { + "href": "https:\/\/jamosova3.mystagingwebsite.com\/wp-json\/wc\/v2\/orders\/1044\/notes" + } + ], + "up": [ + { + "href": "https:\/\/jamosova3.mystagingwebsite.com\/wp-json\/wc\/v2\/orders\/1044" + } + ] + } + }, + { + "id": 2030, + "date_created": "2018-05-25T18:24:42", + "date_created_gmt": "2018-05-25T22:24:42", + "note": "Order status changed from Completed to Processing.", + "customer_note": false, + "_links": { + "self": [ + { + "href": "https:\/\/jamosova3.mystagingwebsite.com\/wp-json\/wc\/v2\/orders\/1044\/notes\/2030" + } + ], + "collection": [ + { + "href": "https:\/\/jamosova3.mystagingwebsite.com\/wp-json\/wc\/v2\/orders\/1044\/notes" + } + ], + "up": [ + { + "href": "https:\/\/jamosova3.mystagingwebsite.com\/wp-json\/wc\/v2\/orders\/1044" + } + ] + } + }, + { + "id": 1992, + "date_created": "2018-05-23T13:39:33", + "date_created_gmt": "2018-05-23T17:39:33", + "note": "Order status changed from Processing to Completed.", + "customer_note": false, + "_links": { + "self": [ + { + "href": "https:\/\/jamosova3.mystagingwebsite.com\/wp-json\/wc\/v2\/orders\/1044\/notes\/1992" + } + ], + "collection": [ + { + "href": "https:\/\/jamosova3.mystagingwebsite.com\/wp-json\/wc\/v2\/orders\/1044\/notes" + } + ], + "up": [ + { + "href": "https:\/\/jamosova3.mystagingwebsite.com\/wp-json\/wc\/v2\/orders\/1044" + } + ] + } + }, + { + "id": 1984, + "date_created": "2018-05-22T17:29:47", + "date_created_gmt": "2018-05-22T21:29:47", + "note": "Order status changed from Completed to Processing.", + "customer_note": false, + "_links": { + "self": [ + { + "href": "https:\/\/jamosova3.mystagingwebsite.com\/wp-json\/wc\/v2\/orders\/1044\/notes\/1984" + } + ], + "collection": [ + { + "href": "https:\/\/jamosova3.mystagingwebsite.com\/wp-json\/wc\/v2\/orders\/1044\/notes" + } + ], + "up": [ + { + "href": "https:\/\/jamosova3.mystagingwebsite.com\/wp-json\/wc\/v2\/orders\/1044" + } + ] + } + }, + { + "id": 1980, + "date_created": "2018-05-22T11:54:39", + "date_created_gmt": "2018-05-22T15:54:39", + "note": "Order status changed from Processing to Completed.", + "customer_note": false, + "_links": { + "self": [ + { + "href": "https:\/\/jamosova3.mystagingwebsite.com\/wp-json\/wc\/v2\/orders\/1044\/notes\/1980" + } + ], + "collection": [ + { + "href": "https:\/\/jamosova3.mystagingwebsite.com\/wp-json\/wc\/v2\/orders\/1044\/notes" + } + ], + "up": [ + { + "href": "https:\/\/jamosova3.mystagingwebsite.com\/wp-json\/wc\/v2\/orders\/1044" + } + ] + } + }, + { + "id": 1976, + "date_created": "2018-05-09T10:39:08", + "date_created_gmt": "2018-05-09T14:39:08", + "note": "Is this a real name?", + "customer_note": true, + "_links": { + "self": [ + { + "href": "https:\/\/jamosova3.mystagingwebsite.com\/wp-json\/wc\/v2\/orders\/1044\/notes\/1976" + } + ], + "collection": [ + { + "href": "https:\/\/jamosova3.mystagingwebsite.com\/wp-json\/wc\/v2\/orders\/1044\/notes" + } + ], + "up": [ + { + "href": "https:\/\/jamosova3.mystagingwebsite.com\/wp-json\/wc\/v2\/orders\/1044" + } + ] + } + }, + { + "id": 1947, + "date_created": "2018-04-30T12:00:28", + "date_created_gmt": "2018-04-30T16:00:28", + "note": "Order has been exported to Shipstation", + "customer_note": false, + "_links": { + "self": [ + { + "href": "https:\/\/jamosova3.mystagingwebsite.com\/wp-json\/wc\/v2\/orders\/1044\/notes\/1947" + } + ], + "collection": [ + { + "href": "https:\/\/jamosova3.mystagingwebsite.com\/wp-json\/wc\/v2\/orders\/1044\/notes" + } + ], + "up": [ + { + "href": "https:\/\/jamosova3.mystagingwebsite.com\/wp-json\/wc\/v2\/orders\/1044" + } + ] + } + }, + { + "id": 1934, + "date_created": "2018-04-25T09:24:15", + "date_created_gmt": "2018-04-25T13:24:15", + "note": "Stripe charge complete (Charge ID: ch_1CKnbUJK48mSkzFLKh1h23Ln)", + "customer_note": false, + "_links": { + "self": [ + { + "href": "https:\/\/jamosova3.mystagingwebsite.com\/wp-json\/wc\/v2\/orders\/1044\/notes\/1934" + } + ], + "collection": [ + { + "href": "https:\/\/jamosova3.mystagingwebsite.com\/wp-json\/wc\/v2\/orders\/1044\/notes" + } + ], + "up": [ + { + "href": "https:\/\/jamosova3.mystagingwebsite.com\/wp-json\/wc\/v2\/orders\/1044" + } + ] + } + }, + { + "id": 1933, + "date_created": "2018-04-25T09:24:15", + "date_created_gmt": "2018-04-25T13:24:15", + "note": "Fabric (Variable Product) (#264)Fabric: Italian<\/span> stock reduced from 11 to 10.", + "customer_note": false, + "_links": { + "self": [ + { + "href": "https:\/\/jamosova3.mystagingwebsite.com\/wp-json\/wc\/v2\/orders\/1044\/notes\/1933" + } + ], + "collection": [ + { + "href": "https:\/\/jamosova3.mystagingwebsite.com\/wp-json\/wc\/v2\/orders\/1044\/notes" + } + ], + "up": [ + { + "href": "https:\/\/jamosova3.mystagingwebsite.com\/wp-json\/wc\/v2\/orders\/1044" + } + ] + } + }, + { + "id": 1932, + "date_created": "2018-04-25T09:24:15", + "date_created_gmt": "2018-04-25T13:24:15", + "note": "Order status changed from Pending payment to Processing.", + "customer_note": false, + "_links": { + "self": [ + { + "href": "https:\/\/jamosova3.mystagingwebsite.com\/wp-json\/wc\/v2\/orders\/1044\/notes\/1932" + } + ], + "collection": [ + { + "href": "https:\/\/jamosova3.mystagingwebsite.com\/wp-json\/wc\/v2\/orders\/1044\/notes" + } + ], + "up": [ + { + "href": "https:\/\/jamosova3.mystagingwebsite.com\/wp-json\/wc\/v2\/orders\/1044" + } + ] + } + } + ] +} diff --git a/Networking/NetworkingTests/Responses/order.json b/Networking/NetworkingTests/Responses/order.json index a5a9f2a9bd9..a63ba4c55ec 100644 --- a/Networking/NetworkingTests/Responses/order.json +++ b/Networking/NetworkingTests/Responses/order.json @@ -17,10 +17,10 @@ "shipping_total": "0.00", "shipping_tax": "0.00", "cart_tax": "0.00", - "total": "100.00", - "total_tax": "0.00", + "total": "102.00", + "total_tax": "2.00", "prices_include_tax": false, - "customer_id": 0, + "customer_id": 100, "customer_ip_address": "174.227.1.93", "customer_user_agent": "mozilla\/5.0 (macintosh; intel mac os x 10_12_6) applewebkit\/537.36 (khtml, like gecko) chrome\/63.0.3239.132 safari\/537.36", "customer_note": "", @@ -32,7 +32,7 @@ "address_2": "", "city": "Omaha", "state": "NE", - "postcode": "9191", + "postcode": "68124", "country": "US", "email": "scrambled", "phone": "99999999" @@ -41,7 +41,7 @@ "first_name": "Maria", "last_name": "Scrambled", "company": "Logged Out", - "address_1": "2309 S 89ct", + "address_1": "9999 Scrambled", "address_2": "", "city": "Omaha", "state": "NE", @@ -87,9 +87,9 @@ "quantity": 1, "tax_class": "", "subtotal": "100.00", - "subtotal_tax": "0.00", + "subtotal_tax": "2.00", "total": "100.00", - "total_tax": "0.00", + "total_tax": "2.00", "taxes": [ ], diff --git a/WooCommerce/Classes/Model/OrderNote.swift b/WooCommerce/Classes/Model/OrderNote.swift deleted file mode 100644 index d8047f02ce1..00000000000 --- a/WooCommerce/Classes/Model/OrderNote.swift +++ /dev/null @@ -1,33 +0,0 @@ -import Foundation - - -struct OrderNote: Decodable { - let identifier: Int - let dateCreated: String - let contents: String - let isCustomerNote: Bool - - init(identifier: Int, dateCreated: String, contents: String, isCustomerNote: Bool) { - self.identifier = identifier - self.dateCreated = dateCreated - self.contents = contents - self.isCustomerNote = isCustomerNote - } - - enum OrderNoteStructKeys: String, CodingKey { - case identifier = "id" - case dateCreated = "date_created" - case contents = "note" - case isCustomerNote = "customer_note" - } - - init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: OrderNoteStructKeys.self) - let identifier = try container.decode(Int.self, forKey: .identifier) - let dateCreated = try container.decode(String.self, forKey: .dateCreated) - let contents = try container.decode(String.self, forKey: .contents) - let isCustomerNote = try container.decode(Bool.self, forKey: .isCustomerNote) - - self.init(identifier: identifier, dateCreated: dateCreated, contents: contents, isCustomerNote: isCustomerNote) - } -} diff --git a/WooCommerce/Classes/ViewModels/OrderNoteViewModel.swift b/WooCommerce/Classes/ViewModels/OrderNoteViewModel.swift index 8ce1a335c7a..50c2425066d 100644 --- a/WooCommerce/Classes/ViewModels/OrderNoteViewModel.swift +++ b/WooCommerce/Classes/ViewModels/OrderNoteViewModel.swift @@ -1,16 +1,18 @@ import UIKit import Gridicons +import Yosemite class OrderNoteViewModel { + let iconImage: UIImage let iconColor: UIColor - private let formattedDate: Date? - private let jsonDate: String let statusText: String let contents: String + let dateCreated: Date init(with orderNote: OrderNote) { iconImage = Gridicon.iconOfType(.aside) + if orderNote.isCustomerNote { iconColor = StyleManager.statusPrimaryBoldColor statusText = NSLocalizedString("Note to customer", comment: "Labels an order note to let user know it's visible to the customer") @@ -19,21 +21,15 @@ class OrderNoteViewModel { statusText = NSLocalizedString("Private note", comment: "Labels an order note to let the user know it's private and not seen by the customer") } - jsonDate = orderNote.dateCreated - let format = DateFormatter.Defaults.dateTimeFormatter - formattedDate = format.date(from: orderNote.dateCreated) - - contents = orderNote.contents + dateCreated = orderNote.dateCreated + contents = orderNote.note } - var dateCreated: String? { + var formattedDateCreated: String? { let formatter = DateFormatter() formatter.dateStyle = .medium formatter.timeStyle = .short - if let date = formattedDate { - return formatter.string(from: date) - } - return jsonDate + return formatter.string(from: dateCreated) } } diff --git a/WooCommerce/Classes/ViewRelated/Orders/OrderDetails/OrderNoteTableViewCell.swift b/WooCommerce/Classes/ViewRelated/Orders/OrderDetails/OrderNoteTableViewCell.swift index 0b1a2372aa2..e8a2bac7203 100644 --- a/WooCommerce/Classes/ViewRelated/Orders/OrderDetails/OrderNoteTableViewCell.swift +++ b/WooCommerce/Classes/ViewRelated/Orders/OrderDetails/OrderNoteTableViewCell.swift @@ -59,7 +59,7 @@ extension OrderNoteTableViewCell { func configure(with viewModel: OrderNoteViewModel) { iconButton.setImage(viewModel.iconImage, for: .normal) iconButton.backgroundColor = viewModel.iconColor - dateCreated = viewModel.dateCreated + dateCreated = viewModel.formattedDateCreated statusText = viewModel.statusText contents = viewModel.contents } diff --git a/WooCommerce/Resources/fake data/order-notes-1044.json b/WooCommerce/Resources/fake data/order-notes-1044.json deleted file mode 100644 index aa285631868..00000000000 --- a/WooCommerce/Resources/fake data/order-notes-1044.json +++ /dev/null @@ -1,194 +0,0 @@ -[ - { - "id": 1992, - "date_created": "2018-05-23T13:39:33", - "date_created_gmt": "2018-05-23T17:39:33", - "note": "Order status changed from Processing to Completed.", - "customer_note": false, - "_links": { - "self": [ - { - "href": "https://jamosova3.mystagingwebsite.com/wp-json/wc/v2/orders/1044/notes/1992" - } - ], - "collection": [ - { - "href": "https://jamosova3.mystagingwebsite.com/wp-json/wc/v2/orders/1044/notes" - } - ], - "up": [ - { - "href": "https://jamosova3.mystagingwebsite.com/wp-json/wc/v2/orders/1044" - } - ] - } - }, - { - "id": 1984, - "date_created": "2018-05-22T17:29:47", - "date_created_gmt": "2018-05-22T21:29:47", - "note": "Order status changed from Completed to Processing.", - "customer_note": false, - "_links": { - "self": [ - { - "href": "https://jamosova3.mystagingwebsite.com/wp-json/wc/v2/orders/1044/notes/1984" - } - ], - "collection": [ - { - "href": "https://jamosova3.mystagingwebsite.com/wp-json/wc/v2/orders/1044/notes" - } - ], - "up": [ - { - "href": "https://jamosova3.mystagingwebsite.com/wp-json/wc/v2/orders/1044" - } - ] - } - }, - { - "id": 1980, - "date_created": "2018-05-22T11:54:39", - "date_created_gmt": "2018-05-22T15:54:39", - "note": "Order status changed from Processing to Completed.", - "customer_note": false, - "_links": { - "self": [ - { - "href": "https://jamosova3.mystagingwebsite.com/wp-json/wc/v2/orders/1044/notes/1980" - } - ], - "collection": [ - { - "href": "https://jamosova3.mystagingwebsite.com/wp-json/wc/v2/orders/1044/notes" - } - ], - "up": [ - { - "href": "https://jamosova3.mystagingwebsite.com/wp-json/wc/v2/orders/1044" - } - ] - } - }, - { - "id": 1976, - "date_created": "2018-05-09T10:39:08", - "date_created_gmt": "2018-05-09T14:39:08", - "note": "Is this a real name?", - "customer_note": true, - "_links": { - "self": [ - { - "href": "https://jamosova3.mystagingwebsite.com/wp-json/wc/v2/orders/1044/notes/1976" - } - ], - "collection": [ - { - "href": "https://jamosova3.mystagingwebsite.com/wp-json/wc/v2/orders/1044/notes" - } - ], - "up": [ - { - "href": "https://jamosova3.mystagingwebsite.com/wp-json/wc/v2/orders/1044" - } - ] - } - }, - { - "id": 1947, - "date_created": "2018-04-30T12:00:28", - "date_created_gmt": "2018-04-30T16:00:28", - "note": "Order has been exported to Shipstation", - "customer_note": false, - "_links": { - "self": [ - { - "href": "https://jamosova3.mystagingwebsite.com/wp-json/wc/v2/orders/1044/notes/1947" - } - ], - "collection": [ - { - "href": "https://jamosova3.mystagingwebsite.com/wp-json/wc/v2/orders/1044/notes" - } - ], - "up": [ - { - "href": "https://jamosova3.mystagingwebsite.com/wp-json/wc/v2/orders/1044" - } - ] - } - }, - { - "id": 1932, - "date_created": "2018-04-25T09:24:15", - "date_created_gmt": "2018-04-25T13:24:15", - "note": "Order status changed from Pending payment to Processing.", - "customer_note": false, - "_links": { - "self": [ - { - "href": "https://jamosova3.mystagingwebsite.com/wp-json/wc/v2/orders/1044/notes/1932" - } - ], - "collection": [ - { - "href": "https://jamosova3.mystagingwebsite.com/wp-json/wc/v2/orders/1044/notes" - } - ], - "up": [ - { - "href": "https://jamosova3.mystagingwebsite.com/wp-json/wc/v2/orders/1044" - } - ] - } - }, - { - "id": 1933, - "date_created": "2018-04-25T09:24:15", - "date_created_gmt": "2018-04-25T13:24:15", - "note": "Fabric (Variable Product) (#264)Fabric: Italian stock reduced from 11 to 10.", - "customer_note": false, - "_links": { - "self": [ - { - "href": "https://jamosova3.mystagingwebsite.com/wp-json/wc/v2/orders/1044/notes/1933" - } - ], - "collection": [ - { - "href": "https://jamosova3.mystagingwebsite.com/wp-json/wc/v2/orders/1044/notes" - } - ], - "up": [ - { - "href": "https://jamosova3.mystagingwebsite.com/wp-json/wc/v2/orders/1044" - } - ] - } - }, - { - "id": 1934, - "date_created": "2018-04-25T09:24:15", - "date_created_gmt": "2018-04-25T13:24:15", - "note": "Stripe charge complete (Charge ID: ch_1CKnbUJK48mSkzFLKh1h23Ln)", - "customer_note": false, - "_links": { - "self": [ - { - "href": "https://jamosova3.mystagingwebsite.com/wp-json/wc/v2/orders/1044/notes/1934" - } - ], - "collection": [ - { - "href": "https://jamosova3.mystagingwebsite.com/wp-json/wc/v2/orders/1044/notes" - } - ], - "up": [ - { - "href": "https://jamosova3.mystagingwebsite.com/wp-json/wc/v2/orders/1044" - } - ] - } - } -] diff --git a/WooCommerce/Resources/fake data/order-notes-1107.json b/WooCommerce/Resources/fake data/order-notes-1107.json deleted file mode 100644 index 5fd9bec160f..00000000000 --- a/WooCommerce/Resources/fake data/order-notes-1107.json +++ /dev/null @@ -1,50 +0,0 @@ -[ - { - "id": 1975, - "date_created": "2018-05-08T15:08:01", - "date_created_gmt": "2018-05-08T19:08:01", - "note": "BluePay payment failed (Auth Code: | Message: Declined Sale| Trans ID: 100562157128| AVS: 2| CVV2: _| Trans Type: SALE)", - "customer_note": false, - "_links": { - "self": [ - { - "href": "https://jamosova3.mystagingwebsite.com/wp-json/wc/v2/orders/1107/notes/1975" - } - ], - "collection": [ - { - "href": "https://jamosova3.mystagingwebsite.com/wp-json/wc/v2/orders/1107/notes" - } - ], - "up": [ - { - "href": "https://jamosova3.mystagingwebsite.com/wp-json/wc/v2/orders/1107" - } - ] - } - }, - { - "id": 1974, - "date_created": "2018-05-08T15:06:07", - "date_created_gmt": "2018-05-08T19:06:07", - "note": "BluePay payment failed (Auth Code: | Message: Declined Sale| Trans ID: 100562156356| AVS: 2| CVV2: _| Trans Type: SALE)", - "customer_note": false, - "_links": { - "self": [ - { - "href": "https://jamosova3.mystagingwebsite.com/wp-json/wc/v2/orders/1107/notes/1974" - } - ], - "collection": [ - { - "href": "https://jamosova3.mystagingwebsite.com/wp-json/wc/v2/orders/1107/notes" - } - ], - "up": [ - { - "href": "https://jamosova3.mystagingwebsite.com/wp-json/wc/v2/orders/1107" - } - ] - } - } -] diff --git a/WooCommerce/WooCommerce.xcodeproj/project.pbxproj b/WooCommerce/WooCommerce.xcodeproj/project.pbxproj index 50437b47f09..30cefa99df3 100644 --- a/WooCommerce/WooCommerce.xcodeproj/project.pbxproj +++ b/WooCommerce/WooCommerce.xcodeproj/project.pbxproj @@ -27,7 +27,6 @@ B50911302049E27A007D25DC /* DashboardViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B509112D2049E27A007D25DC /* DashboardViewController.swift */; }; B50911312049E27A007D25DC /* OrdersViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B509112E2049E27A007D25DC /* OrdersViewController.swift */; }; B50911322049E27A007D25DC /* SettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B509112F2049E27A007D25DC /* SettingsViewController.swift */; }; - B50911342049E493007D25DC /* OrderNote.swift in Sources */ = {isa = PBXBuildFile; fileRef = B50911332049E493007D25DC /* OrderNote.swift */; }; B53B898920D450AF00EDB467 /* SessionManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B53B898820D450AF00EDB467 /* SessionManagerTests.swift */; }; B53B898D20D462A000EDB467 /* StoresManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = B53B898C20D462A000EDB467 /* StoresManager.swift */; }; B54175F220D4C15D0083BB8C /* CoreDataManager+Woo.swift in Sources */ = {isa = PBXBuildFile; fileRef = B54175F120D4C15D0083BB8C /* CoreDataManager+Woo.swift */; }; @@ -72,8 +71,6 @@ CE1EC8CA20B479F1009762BF /* TwoColumnLabelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE1EC8C920B479F1009762BF /* TwoColumnLabelView.swift */; }; CE1EC8CF20B6FD53009762BF /* FootnoteView.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE1EC8CE20B6FD53009762BF /* FootnoteView.xib */; }; CE1EC8D120B6FE39009762BF /* FootnoteView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE1EC8D020B6FE39009762BF /* FootnoteView.swift */; }; - CE1EC8DB20B76088009762BF /* order-notes-1107.json in Resources */ = {isa = PBXBuildFile; fileRef = CE1EC8DA20B76088009762BF /* order-notes-1107.json */; }; - CE1EC8DF20B76651009762BF /* order-notes-1044.json in Resources */ = {isa = PBXBuildFile; fileRef = CE1EC8DE20B76651009762BF /* order-notes-1044.json */; }; CE1EC8E920B8A3F5009762BF /* OrderNoteViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE1EC8E820B8A3F5009762BF /* OrderNoteViewModel.swift */; }; CE1EC8EC20B8A3FF009762BF /* AddItemTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE1EC8EA20B8A3FF009762BF /* AddItemTableViewCell.swift */; }; CE1EC8F020B8A408009762BF /* OrderNoteTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE1EC8EE20B8A408009762BF /* OrderNoteTableViewCell.xib */; }; @@ -147,7 +144,6 @@ B509112D2049E27A007D25DC /* DashboardViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DashboardViewController.swift; sourceTree = ""; }; B509112E2049E27A007D25DC /* OrdersViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OrdersViewController.swift; sourceTree = ""; }; B509112F2049E27A007D25DC /* SettingsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsViewController.swift; sourceTree = ""; }; - B50911332049E493007D25DC /* OrderNote.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OrderNote.swift; sourceTree = ""; }; B53B898820D450AF00EDB467 /* SessionManagerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionManagerTests.swift; sourceTree = ""; }; B53B898C20D462A000EDB467 /* StoresManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StoresManager.swift; sourceTree = ""; }; B54175F120D4C15D0083BB8C /* CoreDataManager+Woo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CoreDataManager+Woo.swift"; sourceTree = ""; }; @@ -199,8 +195,6 @@ CE1EC8C920B479F1009762BF /* TwoColumnLabelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TwoColumnLabelView.swift; sourceTree = ""; }; CE1EC8CE20B6FD53009762BF /* FootnoteView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = FootnoteView.xib; path = Classes/ViewRelated/ReusableViews/FootnoteView.xib; sourceTree = SOURCE_ROOT; }; CE1EC8D020B6FE39009762BF /* FootnoteView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FootnoteView.swift; sourceTree = ""; }; - CE1EC8DA20B76088009762BF /* order-notes-1107.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "order-notes-1107.json"; sourceTree = ""; }; - CE1EC8DE20B76651009762BF /* order-notes-1044.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "order-notes-1044.json"; sourceTree = ""; }; CE1EC8E820B8A3F5009762BF /* OrderNoteViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OrderNoteViewModel.swift; sourceTree = ""; }; CE1EC8EA20B8A3FF009762BF /* AddItemTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddItemTableViewCell.swift; sourceTree = ""; }; CE1EC8EB20B8A3FF009762BF /* AddItemTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = AddItemTableViewCell.xib; sourceTree = ""; }; @@ -267,14 +261,6 @@ name = Frameworks; sourceTree = ""; }; - B509112B2049CFDF007D25DC /* Model */ = { - isa = PBXGroup; - children = ( - B50911332049E493007D25DC /* OrderNote.swift */, - ); - path = Model; - sourceTree = ""; - }; B53B898A20D4606400EDB467 /* System */ = { isa = PBXGroup; children = ( @@ -380,7 +366,6 @@ CE1CCB4C20572444000EE3AC /* Extensions */, CE1CCB3E2056F204000EE3AC /* Styles */, B5D1AFBE20BC67B500DB0E8C /* System */, - B509112B2049CFDF007D25DC /* Model */, CE85535B209B5B6A00938BDC /* ViewModels */, B56DB3EF2049C06D00D4AA8E /* ViewRelated */, B53B898B20D4627A00EDB467 /* Yosemite */, @@ -399,7 +384,6 @@ B56DB3D52049BFAA00D4AA8E /* LaunchScreen.storyboard */, B59F38DF20D40A24008C1829 /* WooCommerce.debug.entitlements */, B59F38E020D40A24008C1829 /* WooCommerce.release.entitlements */, - CEE005FF2077CA5A0079161F /* fake data */, ); path = Resources; sourceTree = ""; @@ -504,15 +488,6 @@ path = ReusableViews; sourceTree = ""; }; - CEE005FF2077CA5A0079161F /* fake data */ = { - isa = PBXGroup; - children = ( - CE1EC8DA20B76088009762BF /* order-notes-1107.json */, - CE1EC8DE20B76651009762BF /* order-notes-1044.json */, - ); - path = "fake data"; - sourceTree = ""; - }; CEE006022077D0F80079161F /* OrderDetails */ = { isa = PBXGroup; children = ( @@ -660,7 +635,6 @@ CE855367209BA6A700938BDC /* ShowHideSectionFooter.xib in Resources */, CE1EC8F020B8A408009762BF /* OrderNoteTableViewCell.xib in Resources */, B55D4BFD20B5CDE700D7A50F /* replace_secrets.rb in Resources */, - CE1EC8DB20B76088009762BF /* order-notes-1107.json in Resources */, CEE006062077D1280079161F /* SummaryTableViewCell.xib in Resources */, B559EBB020A0BF8F00836CD4 /* LICENSE in Resources */, CE32B10B20BEDE05006FBCF4 /* TwoColumnSectionHeaderView.xib in Resources */, @@ -669,7 +643,6 @@ CE32B11620BF8779006FBCF4 /* ProductListTableViewCell.xib in Resources */, CE1EC8C620B46819009762BF /* PaymentTableViewCell.xib in Resources */, B56DB3CF2049BFAA00D4AA8E /* Main.storyboard in Resources */, - CE1EC8DF20B76651009762BF /* order-notes-1044.json in Resources */, CE1EC8C820B478B6009762BF /* TwoColumnLabelView.xib in Resources */, CE855365209BA6A700938BDC /* CustomerInfoTableViewCell.xib in Resources */, ); @@ -858,7 +831,6 @@ CE1EC8EC20B8A3FF009762BF /* AddItemTableViewCell.swift in Sources */, 7403F7E220EC04070097198F /* OrderStatusViewModel.swift in Sources */, CE32B10D20BEDE1C006FBCF4 /* TwoColumnSectionHeaderView.swift in Sources */, - B50911342049E493007D25DC /* OrderNote.swift in Sources */, B5D1AFBA20BC515600DB0E8C /* UIColor+Woo.swift in Sources */, CE1CCB4B20570B1F000EE3AC /* OrderListCell.swift in Sources */, B5AA7B3D20ED5D15004DA14F /* SessionManager.swift in Sources */, diff --git a/Yosemite/Yosemite/Model/Model.swift b/Yosemite/Yosemite/Model/Model.swift index 623d2eb484e..36fed62964d 100644 --- a/Yosemite/Yosemite/Model/Model.swift +++ b/Yosemite/Yosemite/Model/Model.swift @@ -12,3 +12,4 @@ public typealias Order = Networking.Order public typealias OrderItem = Networking.OrderItem public typealias OrderStatus = Networking.OrderStatus public typealias OrderCouponLine = Networking.OrderCouponLine +public typealias OrderNote = Networking.OrderNote