diff --git a/Networking/Networking.xcodeproj/project.pbxproj b/Networking/Networking.xcodeproj/project.pbxproj index 6619872a11a..3b7db950036 100644 --- a/Networking/Networking.xcodeproj/project.pbxproj +++ b/Networking/Networking.xcodeproj/project.pbxproj @@ -696,9 +696,9 @@ 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 */; }; - DE42F96F296BC9A700D514C2 /* countries-without-data.json in Resources */ = {isa = PBXBuildFile; fileRef = DE42F96E296BC9A700D514C2 /* countries-without-data.json */; }; DE42F96B296BC23800D514C2 /* customer-without-data.json in Resources */ = {isa = PBXBuildFile; fileRef = DE42F96A296BC23800D514C2 /* customer-without-data.json */; }; DE42F96D296BC67E00D514C2 /* wc-analytics-customers-without-data.json in Resources */ = {isa = PBXBuildFile; fileRef = DE42F96C296BC67E00D514C2 /* wc-analytics-customers-without-data.json */; }; + DE42F96F296BC9A700D514C2 /* countries-without-data.json in Resources */ = {isa = PBXBuildFile; fileRef = DE42F96E296BC9A700D514C2 /* countries-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 */; }; @@ -714,6 +714,8 @@ DE97C3922861B8E20042E973 /* CouponEncoderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE97C3912861B8E20042E973 /* CouponEncoderTests.swift */; }; DE9D6BCC270D769C00BA6562 /* shipping-label-address-without-name-validation-success.json in Resources */ = {isa = PBXBuildFile; fileRef = DE9D6BCB270D769B00BA6562 /* shipping-label-address-without-name-validation-success.json */; }; DE9DEEF5291CF1B40070AD7C /* site-plugin-without-envelope.json in Resources */ = {isa = PBXBuildFile; fileRef = DE9DEEF4291CF1B40070AD7C /* site-plugin-without-envelope.json */; }; + DEA6B1C4296C0F45005AA5E9 /* payment-gateway-cod-without-data.json in Resources */ = {isa = PBXBuildFile; fileRef = DEA6B1C3296C0F45005AA5E9 /* payment-gateway-cod-without-data.json */; }; + DEA6B1C6296C13FB005AA5E9 /* payment-gateway-list-without-data.json in Resources */ = {isa = PBXBuildFile; fileRef = DEA6B1C5296C13FB005AA5E9 /* payment-gateway-list-without-data.json */; }; DEC2961C26BBE764005A056B /* ShippingLabelCustomsForm.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEC2961B26BBE764005A056B /* ShippingLabelCustomsForm.swift */; }; DEC51A95274CDA52009F3DF4 /* SitePluginMapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEC51A94274CDA52009F3DF4 /* SitePluginMapper.swift */; }; DEC51A97274DD962009F3DF4 /* plugin.json in Resources */ = {isa = PBXBuildFile; fileRef = DEC51A96274DD962009F3DF4 /* plugin.json */; }; @@ -1510,9 +1512,9 @@ 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 = ""; }; - DE42F96E296BC9A700D514C2 /* countries-without-data.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "countries-without-data.json"; sourceTree = ""; }; DE42F96A296BC23800D514C2 /* customer-without-data.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "customer-without-data.json"; sourceTree = ""; }; DE42F96C296BC67E00D514C2 /* wc-analytics-customers-without-data.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "wc-analytics-customers-without-data.json"; sourceTree = ""; }; + DE42F96E296BC9A700D514C2 /* countries-without-data.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "countries-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 = ""; }; @@ -1528,6 +1530,8 @@ DE97C3912861B8E20042E973 /* CouponEncoderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CouponEncoderTests.swift; sourceTree = ""; }; DE9D6BCB270D769B00BA6562 /* shipping-label-address-without-name-validation-success.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "shipping-label-address-without-name-validation-success.json"; sourceTree = ""; }; DE9DEEF4291CF1B40070AD7C /* site-plugin-without-envelope.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "site-plugin-without-envelope.json"; sourceTree = ""; }; + DEA6B1C3296C0F45005AA5E9 /* payment-gateway-cod-without-data.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "payment-gateway-cod-without-data.json"; sourceTree = ""; }; + DEA6B1C5296C13FB005AA5E9 /* payment-gateway-list-without-data.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "payment-gateway-list-without-data.json"; sourceTree = ""; }; DEC2961B26BBE764005A056B /* ShippingLabelCustomsForm.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShippingLabelCustomsForm.swift; sourceTree = ""; }; DEC51A94274CDA52009F3DF4 /* SitePluginMapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SitePluginMapper.swift; sourceTree = ""; }; DEC51A96274DD962009F3DF4 /* plugin.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = plugin.json; sourceTree = ""; }; @@ -2133,6 +2137,8 @@ B559EBA820A0B5B100836CD4 /* Responses */ = { isa = PBXGroup; children = ( + DEA6B1C5296C13FB005AA5E9 /* payment-gateway-list-without-data.json */, + DEA6B1C3296C0F45005AA5E9 /* payment-gateway-cod-without-data.json */, DE42F96E296BC9A700D514C2 /* countries-without-data.json */, DE42F96C296BC67E00D514C2 /* wc-analytics-customers-without-data.json */, DE42F96A296BC23800D514C2 /* customer-without-data.json */, @@ -2952,6 +2958,7 @@ CE20179320E3EFA7005B4C18 /* broken-orders.json in Resources */, D8FBFF2722D529F2006E3336 /* order-stats-v4-month.json in Resources */, 02EF1672292F0D1900D90AD6 /* load-plan-success.json in Resources */, + DEA6B1C6296C13FB005AA5E9 /* payment-gateway-list-without-data.json in Resources */, 2685C0DE263B5A4200D9EE97 /* add-on-groups.json in Resources */, DEC51A9B274E3206009F3DF4 /* plugin-inactive.json in Resources */, CCF48B382628AEAE0034EA83 /* shipping-label-account-settings-no-payment-methods.json in Resources */, @@ -3099,6 +3106,7 @@ 74159623224D2C86003C21CF /* settings-product.json in Resources */, 266C7F9225AD3C88006ED243 /* attribute-term.json in Resources */, D865CE6E278CC19A002C8520 /* stripe-location.json in Resources */, + DEA6B1C4296C0F45005AA5E9 /* payment-gateway-cod-without-data.json in Resources */, CEC4BF93234E7EE0008D9195 /* refunds-all.json in Resources */, D8FBFF1522D3BE09006E3336 /* order-stats-v4-defaults.json in Resources */, 7495AACF225D366D00801A89 /* variation-as-product.json in Resources */, diff --git a/Networking/Networking/Mapper/PaymentGatewayListMapper.swift b/Networking/Networking/Mapper/PaymentGatewayListMapper.swift index 6e6d8b5d7dd..1254fa4cbe1 100644 --- a/Networking/Networking/Mapper/PaymentGatewayListMapper.swift +++ b/Networking/Networking/Mapper/PaymentGatewayListMapper.swift @@ -17,7 +17,11 @@ struct PaymentGatewayListMapper: Mapper { decoder.userInfo = [ .siteID: siteID, ] - return try decoder.decode(PaymentGatewayListEnvelope.self, from: response).paymentGateways + do { + return try decoder.decode(PaymentGatewayListEnvelope.self, from: response).paymentGateways + } catch { + return try decoder.decode([PaymentGateway].self, from: response) + } } } diff --git a/Networking/Networking/Mapper/PaymentGatewayMapper.swift b/Networking/Networking/Mapper/PaymentGatewayMapper.swift index 359643d7fda..c8fc67befc0 100644 --- a/Networking/Networking/Mapper/PaymentGatewayMapper.swift +++ b/Networking/Networking/Mapper/PaymentGatewayMapper.swift @@ -17,7 +17,11 @@ struct PaymentGatewayMapper: Mapper { decoder.userInfo = [ .siteID: siteID, ] - return try decoder.decode(PaymentGatewayEnvelope.self, from: response).paymentGateway + do { + return try decoder.decode(PaymentGatewayEnvelope.self, from: response).paymentGateway + } catch { + return try decoder.decode(PaymentGateway.self, from: response) + } } } diff --git a/Networking/Networking/Remote/PaymentGatewayRemote.swift b/Networking/Networking/Remote/PaymentGatewayRemote.swift index 792944ff0ff..24d6452784e 100644 --- a/Networking/Networking/Remote/PaymentGatewayRemote.swift +++ b/Networking/Networking/Remote/PaymentGatewayRemote.swift @@ -11,7 +11,11 @@ public class PaymentGatewayRemote: Remote { /// - completion: Closure to be executed upon completion. /// public func loadAllPaymentGateways(siteID: Int64, completion: @escaping (Result<[PaymentGateway], Error>) -> Void) { - let request = JetpackRequest(wooApiVersion: .mark3, method: .get, siteID: siteID, path: Constants.path) + let request = JetpackRequest(wooApiVersion: .mark3, + method: .get, + siteID: siteID, + path: Constants.path, + availableAsRESTRequest: true) let mapper = PaymentGatewayListMapper(siteID: siteID) enqueue(request, mapper: mapper, completion: completion) } @@ -35,7 +39,8 @@ public class PaymentGatewayRemote: Remote { method: .put, siteID: siteID, path: path, - parameters: parameters) + parameters: parameters, + availableAsRESTRequest: true) let mapper = PaymentGatewayMapper(siteID: siteID) diff --git a/Networking/NetworkingTests/Mapper/PaymentGatewayListMapperTests.swift b/Networking/NetworkingTests/Mapper/PaymentGatewayListMapperTests.swift index a9af605b6c9..65578a4baab 100644 --- a/Networking/NetworkingTests/Mapper/PaymentGatewayListMapperTests.swift +++ b/Networking/NetworkingTests/Mapper/PaymentGatewayListMapperTests.swift @@ -19,6 +19,18 @@ final class PaymentGatewayListMapperTests: XCTestCase { // Then assertEqual(expectedGateways, gateways) } + + func test_payment_gateway_list_is_decoded_from_json_response_without_data_envelope() throws { + // Given + let jsonData = try XCTUnwrap(Loader.contentsOf("payment-gateway-list-without-data")) + let expectedGateways = [Self.bankTransferGateway, Self.checkGateway, Self.cashGateway, Self.paypalGateway] + + // When + let gateways = try PaymentGatewayListMapper(siteID: Self.sampleSiteID).map(response: jsonData) + + // Then + assertEqual(expectedGateways, gateways) + } } // MARK: Private Helpers diff --git a/Networking/NetworkingTests/Mapper/PaymentGatewayMapperTests.swift b/Networking/NetworkingTests/Mapper/PaymentGatewayMapperTests.swift index 8bf9f64bd20..a438743ba4b 100644 --- a/Networking/NetworkingTests/Mapper/PaymentGatewayMapperTests.swift +++ b/Networking/NetworkingTests/Mapper/PaymentGatewayMapperTests.swift @@ -36,6 +36,22 @@ final class PaymentGatewayMapperTests: XCTestCase { XCTAssertEqual(paymentGateway, expectedPaymentGateway) } + + /// Verifies that the fields are all parsed correctly + /// + func test_PaymentGatewaysList_map_parses_all_fields_in_result_when_response_has_no_data_envelope() throws { + let paymentGateway = try mapRetrievePaymentGatewayResponseWithoutDataEnvelope() + + let expectedPaymentGateway = PaymentGateway(siteID: dummySiteID, + gatewayID: "cod", + title: "Cash on delivery", + description: "Pay with cash upon delivery.", + enabled: true, + features: [.products], + instructions: "Pay with cash upon delivery.") + + XCTAssertEqual(paymentGateway, expectedPaymentGateway) + } } @@ -53,11 +69,17 @@ private extension PaymentGatewayMapperTests { return try PaymentGatewayMapper(siteID: dummySiteID).map(response: response) } - /// Returns the PaymentGatewayMapper output from `payment-gateway-cod.json` + /// Returns the PaymentGateway output from `payment-gateway-cod.json` /// func mapRetrievePaymentGatewayResponse() throws -> PaymentGateway { return try mapPaymentGateway(from: "payment-gateway-cod") } + /// Returns the PaymentGateway output from `payment-gateway-cod-without-data.json` + /// + func mapRetrievePaymentGatewayResponseWithoutDataEnvelope() throws -> PaymentGateway { + return try mapPaymentGateway(from: "payment-gateway-cod-without-data") + } + struct FileNotFoundError: Error {} } diff --git a/Networking/NetworkingTests/Responses/payment-gateway-cod-without-data.json b/Networking/NetworkingTests/Responses/payment-gateway-cod-without-data.json new file mode 100644 index 00000000000..d4d25fbd1f8 --- /dev/null +++ b/Networking/NetworkingTests/Responses/payment-gateway-cod-without-data.json @@ -0,0 +1,67 @@ +{ + "id": "cod", + "title": "Cash on delivery", + "description": "Pay with cash upon delivery.", + "order": "", + "enabled": true, + "method_title": "Cash on delivery", + "method_description": "Have your customers pay with cash (or by other means) upon delivery.", + "method_supports": [ + "products" + ], + "settings": { + "title": { + "id": "title", + "label": "Title", + "description": "Payment method description that the customer will see on your checkout.", + "type": "safe_text", + "value": "Cash on delivery", + "default": "Cash on delivery", + "tip": "Payment method description that the customer will see on your checkout.", + "placeholder": "" + }, + "instructions": { + "id": "instructions", + "label": "Instructions", + "description": "Instructions that will be added to the thank you page.", + "type": "textarea", + "value": "Pay with cash upon delivery.", + "default": "Pay with cash upon delivery.", + "tip": "Instructions that will be added to the thank you page.", + "placeholder": "" + }, + "enable_for_methods": { + "id": "enable_for_methods", + "label": "Enable for shipping methods", + "description": "If COD is only available for certain methods, set it up here. Leave blank to enable for all methods.", + "type": "multiselect", + "value": "", + "default": "", + "tip": "If COD is only available for certain methods, set it up here. Leave blank to enable for all methods.", + "placeholder": "", + "options": { + "Flat rate": { + "flat_rate": "Any "Flat rate" method", + "flat_rate:1": "Other locations – Flat rate (#1)" + }, + "Free shipping": { + "free_shipping": "Any "Free shipping" method", + "free_shipping:4": "Other locations – Free shipping (#4)" + }, + "Local pickup": { + "local_pickup": "Any "Local pickup" method" + } + } + }, + "enable_for_virtual": { + "id": "enable_for_virtual", + "label": "Accept COD if the order is virtual", + "description": "", + "type": "checkbox", + "value": "yes", + "default": "yes", + "tip": "", + "placeholder": "" + } + } +} diff --git a/Networking/NetworkingTests/Responses/payment-gateway-list-without-data.json b/Networking/NetworkingTests/Responses/payment-gateway-list-without-data.json new file mode 100644 index 00000000000..52c68b476ee --- /dev/null +++ b/Networking/NetworkingTests/Responses/payment-gateway-list-without-data.json @@ -0,0 +1,106 @@ +[ + { + "id": "bacs", + "title": "Direct bank transfer", + "description": "Make your payment directly into our bank account. Please use your Order ID as the payment reference. Your order will not be shipped until the funds have cleared in our account.", + "order": "", + "enabled": false, + "method_title": "Direct bank transfer", + "method_description": "Take payments in person via BACS. More commonly known as direct bank/wire transfer", + "method_supports": [ + "products" + ] + }, + { + "id": "cheque", + "title": "Check payments", + "description": "Please send a check to Store Name, Store Street, Store Town, Store State / County, Store Postcode.", + "order": "", + "enabled": false, + "method_title": "Check payments", + "method_description": "Take payments in person via checks. This offline gateway can also be useful to test purchases.", + "method_supports": [ + "products" + ] + }, + { + "id": "cod", + "title": "Cash on delivery", + "description": "Pay with cash upon delivery.", + "order": "", + "enabled": true, + "method_title": "Cash on delivery", + "method_description": "Have your customers pay with cash (or by other means) upon delivery.", + "method_supports": [ + "products" + ], + "settings": { + "title": { + "id": "title", + "label": "Title", + "description": "Payment method description that the customer will see on your checkout.", + "type": "safe_text", + "value": "Cash on delivery", + "default": "Cash on delivery", + "tip": "Payment method description that the customer will see on your checkout.", + "placeholder": "" + }, + "instructions": { + "id": "instructions", + "label": "Instructions", + "description": "Instructions that will be added to the thank you page.", + "type": "textarea", + "value": "Pay with cash upon delivery.", + "default": "Pay with cash upon delivery.", + "tip": "Instructions that will be added to the thank you page.", + "placeholder": "" + }, + "enable_for_methods": { + "id": "enable_for_methods", + "label": "Enable for shipping methods", + "description": "If COD is only available for certain methods, set it up here. Leave blank to enable for all methods.", + "type": "multiselect", + "value": "", + "default": "", + "tip": "If COD is only available for certain methods, set it up here. Leave blank to enable for all methods.", + "placeholder": "", + "options": { + "Flat rate": { + "flat_rate": "Any "Flat rate" method", + "flat_rate:1": "Other locations – Flat rate (#1)" + }, + "Free shipping": { + "free_shipping": "Any "Free shipping" method", + "free_shipping:4": "Other locations – Free shipping (#4)" + }, + "Local pickup": { + "local_pickup": "Any "Local pickup" method" + } + } + }, + "enable_for_virtual": { + "id": "enable_for_virtual", + "label": "Accept COD if the order is virtual", + "description": "", + "type": "checkbox", + "value": "yes", + "default": "yes", + "tip": "", + "placeholder": "" + } + } + }, + { + "id": "paypal", + "title": "PayPal", + "description": "Pay via PayPal; you can pay with your credit card if you don't have a PayPal account.", + "order": "", + "enabled": false, + "method_title": "PayPal Standard", + "method_description": "PayPal Standard redirects customers to PayPal to enter their payment information.", + "method_supports": [ + "products", + "refunds" + ] + } +]