diff --git a/Networking/Networking.xcodeproj/project.pbxproj b/Networking/Networking.xcodeproj/project.pbxproj index 9dd79e1c3a9..e78359326c9 100644 --- a/Networking/Networking.xcodeproj/project.pbxproj +++ b/Networking/Networking.xcodeproj/project.pbxproj @@ -694,6 +694,8 @@ DE42F9602967C88400D514C2 /* report-orders-total-without-data.json in Resources */ = {isa = PBXBuildFile; fileRef = DE42F95E2967C88400D514C2 /* report-orders-total-without-data.json */; }; DE42F9612967C88400D514C2 /* report-orders-total.json in Resources */ = {isa = PBXBuildFile; fileRef = DE42F95F2967C88400D514C2 /* report-orders-total.json */; }; DE42F9632967C8B900D514C2 /* ReportOrderTotalsMapperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE42F9622967C8B900D514C2 /* ReportOrderTotalsMapperTests.swift */; }; + DE42F9652967F34400D514C2 /* refund-single-without-data.json in Resources */ = {isa = PBXBuildFile; fileRef = DE42F9642967F34400D514C2 /* refund-single-without-data.json */; }; + DE42F9672967F61D00D514C2 /* refunds-all-without-data.json in Resources */ = {isa = PBXBuildFile; fileRef = DE42F9662967F61D00D514C2 /* refunds-all-without-data.json */; }; DE50295928C5BD0200551736 /* JetpackUser.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE50295828C5BD0200551736 /* JetpackUser.swift */; }; DE50295B28C5F99700551736 /* DotcomUser.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE50295A28C5F99700551736 /* DotcomUser.swift */; }; DE50295D28C6068B00551736 /* JetpackUserMapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE50295C28C6068B00551736 /* JetpackUserMapper.swift */; }; @@ -1503,6 +1505,8 @@ DE42F95E2967C88400D514C2 /* report-orders-total-without-data.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = "report-orders-total-without-data.json"; path = "NetworkingTests/Responses/report-orders-total-without-data.json"; sourceTree = SOURCE_ROOT; }; DE42F95F2967C88400D514C2 /* report-orders-total.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = "report-orders-total.json"; path = "NetworkingTests/Responses/report-orders-total.json"; sourceTree = SOURCE_ROOT; }; DE42F9622967C8B900D514C2 /* ReportOrderTotalsMapperTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportOrderTotalsMapperTests.swift; sourceTree = ""; }; + DE42F9642967F34400D514C2 /* refund-single-without-data.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "refund-single-without-data.json"; sourceTree = ""; }; + DE42F9662967F61D00D514C2 /* refunds-all-without-data.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "refunds-all-without-data.json"; sourceTree = ""; }; DE50295828C5BD0200551736 /* JetpackUser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JetpackUser.swift; sourceTree = ""; }; DE50295A28C5F99700551736 /* DotcomUser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DotcomUser.swift; sourceTree = ""; }; DE50295C28C6068B00551736 /* JetpackUserMapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JetpackUserMapper.swift; sourceTree = ""; }; @@ -2123,6 +2127,8 @@ B559EBA820A0B5B100836CD4 /* Responses */ = { isa = PBXGroup; children = ( + DE42F9662967F61D00D514C2 /* refunds-all-without-data.json */, + DE42F9642967F34400D514C2 /* refund-single-without-data.json */, EE80A24B29556F1D003591E4 /* Coupon */, EE338A0A294AF92A00183934 /* AppliicationPassword */, DE9DEEF4291CF1B40070AD7C /* site-plugin-without-envelope.json */, @@ -2961,6 +2967,7 @@ 31A451CC27863A2E00FE81AA /* stripe-account-rejected-fraud.json in Resources */, 31A451D827863A2E00FE81AA /* stripe-account-restricted-overdue.json in Resources */, D865CE69278CA245002C8520 /* stripe-payment-intent-unknown-status.json in Resources */, + DE42F9652967F34400D514C2 /* refund-single-without-data.json in Resources */, 0205021C27C86B9700FB1C6B /* inbox-note-without-isRead.json in Resources */, 24F98C622502EFF600F49B68 /* feature-flags-load-all.json in Resources */, DE50296128C609A300551736 /* jetpack-connected-user.json in Resources */, @@ -3067,6 +3074,7 @@ 31054718262E2F5E00C5C02B /* wcpay-payment-intent-requires-confirmation.json in Resources */, D8FBFF2922D52AFB006E3336 /* order-stats-v4-year.json in Resources */, D865CE65278CA202002C8520 /* stripe-payment-intent-canceled.json in Resources */, + DE42F9672967F61D00D514C2 /* refunds-all-without-data.json in Resources */, AED8AEBA272A97B400663FCC /* null-data.json in Resources */, 02BA23CA22EEF62C009539E7 /* order-stats-v4-wcadmin-activated.json in Resources */, 31A451D627863A2E00FE81AA /* stripe-account-unknown-status.json in Resources */, diff --git a/Networking/Networking/Mapper/RefundListMapper.swift b/Networking/Networking/Mapper/RefundListMapper.swift index 798d97df2b8..efff7f0aa69 100644 --- a/Networking/Networking/Mapper/RefundListMapper.swift +++ b/Networking/Networking/Mapper/RefundListMapper.swift @@ -27,7 +27,11 @@ struct RefundListMapper: Mapper { .orderID: orderID ] - return try decoder.decode(RefundsEnvelope.self, from: response).refunds + do { + return try decoder.decode(RefundsEnvelope.self, from: response).refunds + } catch { + return try decoder.decode([Refund].self, from: response) + } } } diff --git a/Networking/Networking/Mapper/RefundMapper.swift b/Networking/Networking/Mapper/RefundMapper.swift index ffa987ce2f2..717642e5789 100644 --- a/Networking/Networking/Mapper/RefundMapper.swift +++ b/Networking/Networking/Mapper/RefundMapper.swift @@ -27,7 +27,11 @@ struct RefundMapper: Mapper { .orderID: orderID ] - return try decoder.decode(RefundEnvelope.self, from: response).refund + do { + return try decoder.decode(RefundEnvelope.self, from: response).refund + } catch { + return try decoder.decode(Refund.self, from: response) + } } /// (Attempts) to encode a Refund object into JSONEncoded data. diff --git a/Networking/Networking/Remote/RefundsRemote.swift b/Networking/Networking/Remote/RefundsRemote.swift index d85d2e6902e..7698ed4e6c5 100644 --- a/Networking/Networking/Remote/RefundsRemote.swift +++ b/Networking/Networking/Remote/RefundsRemote.swift @@ -28,7 +28,12 @@ public final class RefundsRemote: Remote { ParameterKey.contextKey: context ] let path = "\(Path.orders)/" + String(orderID) + "/" + "\(Path.refunds)" - let request = JetpackRequest(wooApiVersion: .mark3, method: .get, siteID: siteID, path: path, parameters: parameters) + let request = JetpackRequest(wooApiVersion: .mark3, + method: .get, + siteID: siteID, + path: path, + parameters: parameters, + availableAsRESTRequest: true) let mapper = RefundListMapper(siteID: siteID, orderID: orderID) enqueue(request, mapper: mapper, completion: completion) @@ -49,7 +54,12 @@ public final class RefundsRemote: Remote { let stringOfRefundIDs = refundIDs.sortedUniqueIntToString() let parameters = [ ParameterKey.include: stringOfRefundIDs ] let path = "\(Path.orders)/" + String(orderID) + "/" + "\(Path.refunds)" - let request = JetpackRequest(wooApiVersion: .mark3, method: .get, siteID: siteID, path: path, parameters: parameters) + let request = JetpackRequest(wooApiVersion: .mark3, + method: .get, + siteID: siteID, + path: path, + parameters: parameters, + availableAsRESTRequest: true) let mapper = RefundListMapper(siteID: siteID, orderID: orderID) enqueue(request, mapper: mapper, completion: completion) @@ -68,7 +78,12 @@ public final class RefundsRemote: Remote { refundID: Int64, completion: @escaping (Refund?, Error?) -> Void) { let path = Path.orders + "/" + String(orderID) + "/" + Path.refunds + "/" + String(refundID) - let request = JetpackRequest(wooApiVersion: .mark3, method: .get, siteID: siteID, path: path, parameters: nil) + let request = JetpackRequest(wooApiVersion: .mark3, + method: .get, + siteID: siteID, + path: path, + parameters: nil, + availableAsRESTRequest: true) let mapper = RefundMapper(siteID: siteID, orderID: orderID) enqueue(request, mapper: mapper, completion: completion) @@ -92,7 +107,12 @@ public final class RefundsRemote: Remote { do { let encodedJson = try mapper.map(refund: refund) let parameters: [String: Any]? = try JSONSerialization.jsonObject(with: encodedJson, options: []) as? [String: Any] - let request = JetpackRequest(wooApiVersion: .mark3, method: .post, siteID: siteID, path: path, parameters: parameters) + let request = JetpackRequest(wooApiVersion: .mark3, + method: .post, + siteID: siteID, + path: path, + parameters: parameters, + availableAsRESTRequest: true) enqueue(request, mapper: mapper, completion: completion) } catch { diff --git a/Networking/NetworkingTests/Mapper/RefundListMapperTests.swift b/Networking/NetworkingTests/Mapper/RefundListMapperTests.swift index 57790203559..9ed1c244415 100644 --- a/Networking/NetworkingTests/Mapper/RefundListMapperTests.swift +++ b/Networking/NetworkingTests/Mapper/RefundListMapperTests.swift @@ -17,39 +17,40 @@ final class RefundListMapperTests: XCTestCase { /// Verifies that all the Refund fields are parsed correctly. /// func test_Refund_fields_are_properly_parsed() { - let refunds = mapLoadAllRefundsResponse() - XCTAssertEqual(refunds.count, 2) - - let firstRefund = refunds[0] - XCTAssertEqual(firstRefund.siteID, dummySiteID) - XCTAssertEqual(firstRefund.orderID, orderID) - XCTAssertEqual(firstRefund.refundID, 590) - - let dateCreated = DateFormatter.Defaults.dateTimeFormatter.date(from: "2019-10-09T16:18:23") - XCTAssertEqual(firstRefund.dateCreated, dateCreated) - - XCTAssertEqual(firstRefund.amount, "18.00") - XCTAssertEqual(firstRefund.reason, "Only 1 black hoodie left. Inventory count was off. My bad!") - XCTAssertEqual(firstRefund.refundedByUserID, 1) - - if let isAutomated = firstRefund.isAutomated { - XCTAssertTrue(isAutomated) - } - - let secondRefund = refunds[1] - XCTAssertEqual(secondRefund.siteID, dummySiteID) - XCTAssertEqual(secondRefund.orderID, orderID) - XCTAssertEqual(secondRefund.refundID, 562) - - let dateCreated2 = DateFormatter.Defaults.dateTimeFormatter.date(from: "2019-10-01T19:33:46") - XCTAssertEqual(secondRefund.dateCreated, dateCreated2) - - XCTAssertEqual(secondRefund.amount, "27.00") - XCTAssertEqual(secondRefund.reason, "My pet hamster ate the sleeve off of one of the Blue XL hoodies. Sorry! No longer for sale.") - XCTAssertEqual(secondRefund.refundedByUserID, 1) - - if let isAutomated = secondRefund.isAutomated { - XCTAssertTrue(isAutomated) + let result = [mapLoadAllRefundsResponse(), mapLoadAllRefundsResponseWithoutDataEnvelope()] + for refunds in result { + XCTAssertEqual(refunds.count, 2) + + let firstRefund = refunds[0] + XCTAssertEqual(firstRefund.siteID, dummySiteID) + XCTAssertEqual(firstRefund.orderID, orderID) + XCTAssertEqual(firstRefund.refundID, 590) + + let dateCreated = DateFormatter.Defaults.dateTimeFormatter.date(from: "2019-10-09T16:18:23") + XCTAssertEqual(firstRefund.dateCreated, dateCreated) + + XCTAssertEqual(firstRefund.amount, "18.00") + XCTAssertEqual(firstRefund.reason, "Only 1 black hoodie left. Inventory count was off. My bad!") + XCTAssertEqual(firstRefund.refundedByUserID, 1) + if let isAutomated = firstRefund.isAutomated { + XCTAssertTrue(isAutomated) + } + + let secondRefund = refunds[1] + XCTAssertEqual(secondRefund.siteID, dummySiteID) + XCTAssertEqual(secondRefund.orderID, orderID) + XCTAssertEqual(secondRefund.refundID, 562) + + let dateCreated2 = DateFormatter.Defaults.dateTimeFormatter.date(from: "2019-10-01T19:33:46") + XCTAssertEqual(secondRefund.dateCreated, dateCreated2) + + XCTAssertEqual(secondRefund.amount, "27.00") + XCTAssertEqual(secondRefund.reason, "My pet hamster ate the sleeve off of one of the Blue XL hoodies. Sorry! No longer for sale.") + XCTAssertEqual(secondRefund.refundedByUserID, 1) + + if let isAutomated = secondRefund.isAutomated { + XCTAssertTrue(isAutomated) + } } } @@ -129,4 +130,10 @@ private extension RefundListMapperTests { func mapLoadAllRefundsResponse() -> [Refund] { return mapRefunds(from: "refunds-all") } + + /// Returns the RefundListMapper output upon receiving `refunds-all-without-data` + /// + func mapLoadAllRefundsResponseWithoutDataEnvelope() -> [Refund] { + return mapRefunds(from: "refunds-all-without-data") + } } diff --git a/Networking/NetworkingTests/Mapper/RefundMapperTests.swift b/Networking/NetworkingTests/Mapper/RefundMapperTests.swift index 5f2324544f1..ec346096df4 100644 --- a/Networking/NetworkingTests/Mapper/RefundMapperTests.swift +++ b/Networking/NetworkingTests/Mapper/RefundMapperTests.swift @@ -17,21 +17,24 @@ final class RefundMapperTests: XCTestCase { /// Verifies that all of the Refund fields are parsed correctly. /// func test_Refund_fields_are_properly_parsed() { - guard let refund = mapLoadRefundResponse() else { - XCTFail("No `refund-single.json` file found.") - return + let results = [mapLoadRefundResponse(), mapLoadRefundResponseWithoutDataEnvelope()] + for refund in results { + guard let refund else { + XCTFail("No mock file found.") + return + } + + XCTAssertEqual(refund.siteID, dummySiteID) + XCTAssertEqual(refund.refundID, 562) + + let dateCreated = DateFormatter.Defaults.dateTimeFormatter.date(from: "2019-10-01T19:33:46") + XCTAssertEqual(refund.dateCreated, dateCreated) + + XCTAssertEqual(refund.amount, "27.00") + XCTAssertEqual(refund.reason, "My pet hamster ate the sleeve off of one of the Blue XL hoodies. Sorry! No longer for sale.") + XCTAssertEqual(refund.refundedByUserID, 1) + XCTAssertEqual(refund.isAutomated, true) } - - XCTAssertEqual(refund.siteID, dummySiteID) - XCTAssertEqual(refund.refundID, 562) - - let dateCreated = DateFormatter.Defaults.dateTimeFormatter.date(from: "2019-10-01T19:33:46") - XCTAssertEqual(refund.dateCreated, dateCreated) - - XCTAssertEqual(refund.amount, "27.00") - XCTAssertEqual(refund.reason, "My pet hamster ate the sleeve off of one of the Blue XL hoodies. Sorry! No longer for sale.") - XCTAssertEqual(refund.refundedByUserID, 1) - XCTAssertEqual(refund.isAutomated, true) } /// Verifies that all of the Refunded Order Items are parsed correctly. @@ -160,6 +163,12 @@ private extension RefundMapperTests { return mapRefund(from: "refund-single") } + /// Returns the RefundsMapper output upon receiving `refund-single-without-data` + /// + func mapLoadRefundResponseWithoutDataEnvelope() -> Refund? { + return mapRefund(from: "refund-single-without-data") + } + /// Creates a dummy refund with items and taxes /// func sampleRefund(includeTaxes: Bool) -> Refund { diff --git a/Networking/NetworkingTests/Responses/refund-single-without-data.json b/Networking/NetworkingTests/Responses/refund-single-without-data.json new file mode 100644 index 00000000000..d0b9c94250c --- /dev/null +++ b/Networking/NetworkingTests/Responses/refund-single-without-data.json @@ -0,0 +1,68 @@ +{ + "id": 562, + "date_created": "2019-10-01T19:33:46", + "date_created_gmt": "2019-10-01T19:33:46", + "amount": "27.00", + "reason": "My pet hamster ate the sleeve off of one of the Blue XL hoodies. Sorry! No longer for sale.", + "refunded_by": 1, + "refunded_payment": true, + "meta_data": [], + "line_items": [ + { + "id": 67, + "name": "Ship Your Idea - Blue, XL", + "product_id": 21, + "variation_id": 70, + "quantity": -1, + "tax_class": "", + "subtotal": "-27.00", + "subtotal_tax": "0.00", + "total": "-27.00", + "total_tax": "0.00", + "taxes": [], + "meta_data": [ + { + "id": 605, + "key": "_refunded_item_id", + "value": "65" + } + ], + "sku": "HOODIE-SHIP-YOUR-IDEA-BLUE-XL", + "price": 27 + } + ], + "shipping_lines": [ + { + "id": 189, + "method_title": "Flat rate", + "method_id": "flat_rate", + "instance_id": "1", + "total": "-7.00", + "total_tax": "-0.62", + "taxes": [ + { + "id": 1, + "total": "-0.62", + "subtotal": "" + } + ] + } + ], + "_links": { + "self": [ + { + "href": "https://test.com/wp-json/wc/v3/orders/560/refunds/562" + } + ], + "collection": [ + { + "href": "https://test.com/wp-json/wc/v3/orders/560/refunds" + } + ], + "up": [ + { + "href": "https://test.com/wp-json/wc/v3/orders/560" + } + ] + } +} diff --git a/Networking/NetworkingTests/Responses/refunds-all-without-data.json b/Networking/NetworkingTests/Responses/refunds-all-without-data.json new file mode 100644 index 00000000000..621d94d92df --- /dev/null +++ b/Networking/NetworkingTests/Responses/refunds-all-without-data.json @@ -0,0 +1,104 @@ +[ + { + "id": 590, + "date_created": "2019-10-09T16:18:23", + "date_created_gmt": "2019-10-09T16:18:23", + "amount": "18.00", + "reason": "Only 1 black hoodie left. Inventory count was off. My bad!", + "refunded_by": 1, + "refunded_payment": true, + "meta_data": [], + "line_items": [ + { + "id": 73, + "name": "Ninja Silhouette", + "product_id": 22, + "variation_id": 0, + "quantity": -1, + "tax_class": "", + "subtotal": "-18.00", + "subtotal_tax": "0.00", + "total": "-18.00", + "total_tax": "0.00", + "taxes": [], + "meta_data": [ + { + "id": 659, + "key": "_refunded_item_id", + "value": "60" + } + ], + "sku": "T-SHIRT-NINJA-SILHOUETTE", + "price": 18 + } + ], + "_links": { + "self": [ + { + "href": "https://test.com/wp-json/wc/v3/orders/560/refunds/590" + } + ], + "collection": [ + { + "href": "https://test.com/wp-json/wc/v3/orders/560/refunds" + } + ], + "up": [ + { + "href": "https://test.com/wp-json/wc/v3/orders/560" + } + ] + } + }, + { + "id": 562, + "date_created": "2019-10-01T19:33:46", + "date_created_gmt": "2019-10-01T19:33:46", + "amount": "27.00", + "reason": "My pet hamster ate the sleeve off of one of the Blue XL hoodies. Sorry! No longer for sale.", + "refunded_by": 1, + "refunded_payment": true, + "meta_data": [], + "line_items": [ + { + "id": 67, + "name": "Ship Your Idea - Blue, XL", + "product_id": 21, + "variation_id": 70, + "quantity": -1, + "tax_class": "", + "subtotal": "-27.00", + "subtotal_tax": "0.00", + "total": "-27.00", + "total_tax": "0.00", + "taxes": [], + "meta_data": [ + { + "id": 605, + "key": "_refunded_item_id", + "value": "65" + } + ], + "sku": "HOODIE-SHIP-YOUR-IDEA-BLUE-XL", + "price": 27 + } + ], + "_links": { + "self": [ + { + "href": "https://test.com/wp-json/wc/v3/orders/560/refunds/562" + } + ], + "collection": [ + { + "href": "https://test.com/wp-json/wc/v3/orders/560/refunds" + } + ], + "up": [ + { + "href": "https://test.com/wp-json/wc/v3/orders/560" + } + ] + } + } +]