diff --git a/Networking/Networking/Model/Product/Product.swift b/Networking/Networking/Model/Product/Product.swift index c6ac90d2489..d33ae1f5125 100644 --- a/Networking/Networking/Model/Product/Product.swift +++ b/Networking/Networking/Model/Product/Product.swift @@ -375,7 +375,11 @@ public struct Product: Codable, GeneratedCopiable, Equatable, GeneratedFakeable alternativeTypes: [.decimal(transform: { NSDecimalNumber(decimal: $0).stringValue })]) ?? "" - let onSale = try container.decode(Bool.self, forKey: .onSale) + // Even though WooCommerce Core returns Bool values, + // some plugins alter the field value from Bool to String. + let onSale = container.failsafeDecodeIfPresent(targetType: Bool.self, + forKey: .onSale, + alternativeTypes: [ .string(transform: { NSString(string: $0).boolValue })]) ?? false // Even though a plain install of WooCommerce Core provides string values, // some plugins alter the field value from String to Int or Decimal. @@ -425,7 +429,13 @@ public struct Product: Codable, GeneratedCopiable, Equatable, GeneratedFakeable let stockStatusKey = try container.decode(String.self, forKey: .stockStatusKey) let backordersKey = try container.decode(String.self, forKey: .backordersKey) - let backordersAllowed = try container.decode(Bool.self, forKey: .backordersAllowed) + + // Even though WooCommerce Core returns Bool values, + // some plugins alter the field value from Bool to String. + let backordersAllowed = container.failsafeDecodeIfPresent(targetType: Bool.self, + forKey: .backordersAllowed, + alternativeTypes: [ .string(transform: { NSString(string: $0).boolValue })]) ?? false + let backordered = try container.decode(Bool.self, forKey: .backordered) let soldIndividually = try container.decodeIfPresent(Bool.self, forKey: .soldIndividually) ?? false diff --git a/Networking/Networking/Model/Product/ProductVariation.swift b/Networking/Networking/Model/Product/ProductVariation.swift index 826dedd3f36..5089ca1ea49 100644 --- a/Networking/Networking/Model/Product/ProductVariation.swift +++ b/Networking/Networking/Model/Product/ProductVariation.swift @@ -224,7 +224,12 @@ public struct ProductVariation: Codable, GeneratedCopiable, Equatable, Generated forKey: .regularPrice, alternativeTypes: [.decimal(transform: { NSDecimalNumber(decimal: $0).stringValue })]) ?? "" - let onSale = try container.decode(Bool.self, forKey: .onSale) + + // Even though WooCommerce Core returns Bool values, + // some plugins alter the field value from Bool to String. + let onSale = container.failsafeDecodeIfPresent(targetType: Bool.self, + forKey: .onSale, + alternativeTypes: [ .string(transform: { NSString(string: $0).boolValue })]) ?? false // Even though a plain install of WooCommerce Core provides string values, // some plugins alter the field value from String to Int or Decimal. @@ -273,7 +278,13 @@ public struct ProductVariation: Codable, GeneratedCopiable, Equatable, Generated let stockStatusKey = try container.decode(String.self, forKey: .stockStatusKey) let stockStatus = ProductStockStatus(rawValue: stockStatusKey) let backordersKey = try container.decode(String.self, forKey: .backordersKey) - let backordersAllowed = try container.decode(Bool.self, forKey: .backordersAllowed) + + // Even though WooCommerce Core returns Bool values, + // some plugins alter the field value from Bool to String. + let backordersAllowed = container.failsafeDecodeIfPresent(targetType: Bool.self, + forKey: .backordersAllowed, + alternativeTypes: [ .string(transform: { NSString(string: $0).boolValue })]) ?? false + let backordered = try container.decode(Bool.self, forKey: .backordered) // Even though a plain install of WooCommerce Core provides String values, diff --git a/Networking/NetworkingTests/Mapper/ProductMapperTests.swift b/Networking/NetworkingTests/Mapper/ProductMapperTests.swift index 563f2f6b70c..681dd6ee356 100644 --- a/Networking/NetworkingTests/Mapper/ProductMapperTests.swift +++ b/Networking/NetworkingTests/Mapper/ProductMapperTests.swift @@ -123,6 +123,8 @@ final class ProductMapperTests: XCTestCase { XCTAssertEqual(product.dimensions.width, "33") XCTAssertEqual(product.dimensions.height, "54") XCTAssertEqual(product.downloads.first?.downloadID, "12345") + XCTAssertEqual(product.backordersAllowed, true) + XCTAssertEqual(product.onSale, false) } /// Verifies that the `salePrice` field of the Product are parsed correctly when the product is on sale, and the sale price is an empty string diff --git a/Networking/NetworkingTests/Mapper/ProductVariationMapperTests.swift b/Networking/NetworkingTests/Mapper/ProductVariationMapperTests.swift index cc08e8648bc..e2e18baac5c 100644 --- a/Networking/NetworkingTests/Mapper/ProductVariationMapperTests.swift +++ b/Networking/NetworkingTests/Mapper/ProductVariationMapperTests.swift @@ -42,6 +42,8 @@ final class ProductVariationMapperTests: XCTestCase { XCTAssertEqual(productVariation.permalink, "") XCTAssertEqual(productVariation.sku, "12345") XCTAssertEqual(productVariation.weight, "2.5") + XCTAssertEqual(productVariation.backordersAllowed, true) + XCTAssertEqual(productVariation.onSale, false) } /// Test that the fields for variations of a subscription product are properly parsed. diff --git a/Networking/NetworkingTests/Responses/product-alternative-types.json b/Networking/NetworkingTests/Responses/product-alternative-types.json index dfad347304f..bd1326c7eab 100644 --- a/Networking/NetworkingTests/Responses/product-alternative-types.json +++ b/Networking/NetworkingTests/Responses/product-alternative-types.json @@ -23,7 +23,7 @@ "date_on_sale_to": null, "date_on_sale_to_gmt": "2019-10-27T21:29:59", "price_html": "Free", - "on_sale": false, + "on_sale": "", "purchasable": 1, "total_sales": 0, "virtual": true, @@ -43,7 +43,7 @@ "stock_quantity": null, "stock_status": "instock", "backorders": "no", - "backorders_allowed": false, + "backorders_allowed": "1", "backordered": false, "sold_individually": null, "weight": 213, diff --git a/Networking/NetworkingTests/Responses/product-variation-alternative-types.json b/Networking/NetworkingTests/Responses/product-variation-alternative-types.json index 66ac40b194d..a49b21a5708 100644 --- a/Networking/NetworkingTests/Responses/product-variation-alternative-types.json +++ b/Networking/NetworkingTests/Responses/product-variation-alternative-types.json @@ -16,7 +16,7 @@ "date_on_sale_from_gmt": "2019-10-15T21:30:00", "date_on_sale_to": "2019-10-27T23:59:59", "date_on_sale_to_gmt": "2019-10-27T21:29:59", - "on_sale": false, + "on_sale": "0", "status": "publish", "purchasable": 1, "virtual": false, @@ -30,7 +30,7 @@ "stock_quantity": 16, "stock_status": "instock", "backorders": "notify", - "backorders_allowed": true, + "backorders_allowed": "1", "backordered": false, "weight": 2.5, "dimensions": { diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt index b3766e0a7f4..b6d74c444ea 100644 --- a/RELEASE-NOTES.txt +++ b/RELEASE-NOTES.txt @@ -4,6 +4,7 @@ ----- - [*] Orders: Allow alternative types for the `taxID` in `ShippingLineTax` or `sku` in `OrderItem`, as some third-party plugins alter the type in the API. This helps with the order list not loading due to order decoding errors. [https://github.com/woocommerce/woocommerce-ios/pull/9844] - [*] Payments: Location permissions request is not shown to TTP users who grant "Allow once" permission on first foregrounding the app any more [https://github.com/woocommerce/woocommerce-ios/pull/9821] +- [*] Products: Allow alternative types for the `backordersAllowed` and `onSale` in `Product` and `ProductVariation`, as some third-party plugins alter the types in the API. This helps with the product list not loading due to product decoding errors. [https://github.com/woocommerce/woocommerce-ios/pull/9849] - [*] Products: Allow alternative types for the `sku` and `weight` in `ProductVariation`, as some third-party plugins alter the types in the API. This helps with the product variation list not loading due to product variation decoding errors. [https://github.com/woocommerce/woocommerce-ios/pull/9847] - [*] Products: Allow alternative types for the `sku` and `weight` in `Product`, the dimensions in `ProductDimensions`, and the `downloadID` in `ProductDownload`, as some third-party plugins alter the types in the API. This helps with the product list not loading due to product decoding errors. [https://github.com/woocommerce/woocommerce-ios/pull/9846]