From bd068cd01ef1cd044322d99577edb6271ef2b04a Mon Sep 17 00:00:00 2001 From: Huong Do Date: Wed, 4 Jan 2023 17:51:51 +0800 Subject: [PATCH 1/4] Add mock data for order stats without data envelope --- .../Networking.xcodeproj/project.pbxproj | 4 ++ .../order-stats-v4-year-without-data.json | 38 +++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 Networking/NetworkingTests/Responses/order-stats-v4-year-without-data.json diff --git a/Networking/Networking.xcodeproj/project.pbxproj b/Networking/Networking.xcodeproj/project.pbxproj index 9ca45e24fcb..582a4340a56 100644 --- a/Networking/Networking.xcodeproj/project.pbxproj +++ b/Networking/Networking.xcodeproj/project.pbxproj @@ -724,6 +724,7 @@ DEF13C5029629EEA0024A02B /* user-complete-without-data.json in Resources */ = {isa = PBXBuildFile; fileRef = DEF13C4F29629EEA0024A02B /* user-complete-without-data.json */; }; DEF13C562965689F0024A02B /* LeaderboardListMapperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEF13C552965689F0024A02B /* LeaderboardListMapperTests.swift */; }; DEF13C5A296571150024A02B /* leaderboards-year-without-data.json in Resources */ = {isa = PBXBuildFile; fileRef = DEF13C59296571150024A02B /* leaderboards-year-without-data.json */; }; + DEF13C5C2965812D0024A02B /* order-stats-v4-year-without-data.json in Resources */ = {isa = PBXBuildFile; fileRef = DEF13C5B2965812D0024A02B /* order-stats-v4-year-without-data.json */; }; DEFBA74E29485A7600C35BA9 /* RESTRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEFBA74D29485A7600C35BA9 /* RESTRequest.swift */; }; DEFBA7542949CE6600C35BA9 /* RequestProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEFBA7532949CE6600C35BA9 /* RequestProcessor.swift */; }; DEFBA7562949D17400C35BA9 /* DefaultRequestAuthenticatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEFBA7552949D17300C35BA9 /* DefaultRequestAuthenticatorTests.swift */; }; @@ -1519,6 +1520,7 @@ DEF13C4F29629EEA0024A02B /* user-complete-without-data.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "user-complete-without-data.json"; sourceTree = ""; }; DEF13C552965689F0024A02B /* LeaderboardListMapperTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LeaderboardListMapperTests.swift; sourceTree = ""; }; DEF13C59296571150024A02B /* leaderboards-year-without-data.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "leaderboards-year-without-data.json"; sourceTree = ""; }; + DEF13C5B2965812D0024A02B /* order-stats-v4-year-without-data.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "order-stats-v4-year-without-data.json"; sourceTree = ""; }; DEFBA74D29485A7600C35BA9 /* RESTRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RESTRequest.swift; sourceTree = ""; }; DEFBA7532949CE6600C35BA9 /* RequestProcessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestProcessor.swift; sourceTree = ""; }; DEFBA7552949D17300C35BA9 /* DefaultRequestAuthenticatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultRequestAuthenticatorTests.swift; sourceTree = ""; }; @@ -2195,6 +2197,7 @@ D8FBFF2322D52815006E3336 /* order-stats-v4-daily.json */, D8FBFF2622D529F2006E3336 /* order-stats-v4-month.json */, D8FBFF2822D52AFA006E3336 /* order-stats-v4-year.json */, + DEF13C5B2965812D0024A02B /* order-stats-v4-year-without-data.json */, D8FBFF1422D3BE09006E3336 /* order-stats-v4-defaults.json */, D8FBFF1722D4DDB9006E3336 /* order-stats-v4-hour.json */, D8C11A5B22DFCF8100D4A88D /* order-stats-v4-year-alt.json */, @@ -3099,6 +3102,7 @@ 31A451D127863A2E00FE81AA /* stripe-account-rejected-other.json in Resources */, DEF13C5029629EEA0024A02B /* user-complete-without-data.json in Resources */, 74ABA1CA213F19FE00FFAD30 /* top-performers-year.json in Resources */, + DEF13C5C2965812D0024A02B /* order-stats-v4-year-without-data.json in Resources */, 31A451CF27863A2E00FE81AA /* stripe-account-rejected-terms-of-service.json in Resources */, DE2E8EA9295416C9002E4B14 /* wordpress-site-info.json in Resources */, 268EC45C26C169F600716F5C /* order-with-faulty-attributes.json in Resources */, diff --git a/Networking/NetworkingTests/Responses/order-stats-v4-year-without-data.json b/Networking/NetworkingTests/Responses/order-stats-v4-year-without-data.json new file mode 100644 index 00000000000..69c3a481f7c --- /dev/null +++ b/Networking/NetworkingTests/Responses/order-stats-v4-year-without-data.json @@ -0,0 +1,38 @@ +{ + "totals": { + "orders_count": 3, + "num_items_sold": 5, + "total_sales": 800, + "coupons": 0, + "coupons_count": 0, + "refunds": 0, + "taxes": 0, + "shipping": 0, + "net_revenue": 800, + "products": 2, + "avg_order_value": 266, + "segments": [] + }, + "intervals": [ + { + "interval": "2019", + "date_start": "2019-07-09 00:00:00", + "date_start_gmt": "2019-07-09 00:00:00", + "date_end": "2019-07-09 23:59:59", + "date_end_gmt": "2019-07-09 23:59:59", + "subtotals": { + "orders_count": 3, + "num_items_sold": 5, + "total_sales": 800, + "coupons": 0, + "coupons_count": 0, + "refunds": 0, + "taxes": 0, + "shipping": 0, + "net_revenue": 800, + "avg_order_value": 266, + "segments": [] + } + } + ] +} From f7609a308ac17628a6b7663e2afd70b7b67c3133 Mon Sep 17 00:00:00 2001 From: Huong Do Date: Wed, 4 Jan 2023 17:52:07 +0800 Subject: [PATCH 2/4] Update OrderStatsV4Mapper to parse contents without the data envelope --- Networking/Networking/Mapper/OrderStatsV4Mapper.swift | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Networking/Networking/Mapper/OrderStatsV4Mapper.swift b/Networking/Networking/Mapper/OrderStatsV4Mapper.swift index b615f56df3c..afce5096a83 100644 --- a/Networking/Networking/Mapper/OrderStatsV4Mapper.swift +++ b/Networking/Networking/Mapper/OrderStatsV4Mapper.swift @@ -24,7 +24,11 @@ struct OrderStatsV4Mapper: Mapper { .siteID: siteID, .granularity: granularity ] - return try decoder.decode(OrderStatsV4Envelope.self, from: response).orderStats + do { + return try decoder.decode(OrderStatsV4Envelope.self, from: response).orderStats + } catch { + return try decoder.decode(OrderStatsV4.self, from: response) + } } } From c24ac8e7ad7b7cbefc45041ce15e70089b6b9956 Mon Sep 17 00:00:00 2001 From: Huong Do Date: Wed, 4 Jan 2023 17:52:58 +0800 Subject: [PATCH 3/4] Enable REST request for loadOrderStats --- Networking/Networking/Remote/OrderStatsRemoteV4.swift | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Networking/Networking/Remote/OrderStatsRemoteV4.swift b/Networking/Networking/Remote/OrderStatsRemoteV4.swift index 9244eec15ce..6457da69ba5 100644 --- a/Networking/Networking/Remote/OrderStatsRemoteV4.swift +++ b/Networking/Networking/Remote/OrderStatsRemoteV4.swift @@ -33,7 +33,12 @@ public final class OrderStatsRemoteV4: Remote { ParameterKeys.forceRefresh: forceRefresh ] - let request = JetpackRequest(wooApiVersion: .wcAnalytics, method: .get, siteID: siteID, path: Constants.orderStatsPath, parameters: parameters) + let request = JetpackRequest(wooApiVersion: .wcAnalytics, + method: .get, + siteID: siteID, + path: Constants.orderStatsPath, + parameters: parameters, + availableAsRESTRequest: true) let mapper = OrderStatsV4Mapper(siteID: siteID, granularity: unit) enqueue(request, mapper: mapper, completion: completion) } From 787107356d0ec306c59449ebf4e7210b5b00c118 Mon Sep 17 00:00:00 2001 From: Huong Do Date: Wed, 4 Jan 2023 17:58:59 +0800 Subject: [PATCH 4/4] Update test for parsing order stats without data envelope --- .../Mapper/OrderStatsMapperV4Tests.swift | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/Networking/NetworkingTests/Mapper/OrderStatsMapperV4Tests.swift b/Networking/NetworkingTests/Mapper/OrderStatsMapperV4Tests.swift index 4ed94cc848e..67a4ca9c21d 100644 --- a/Networking/NetworkingTests/Mapper/OrderStatsMapperV4Tests.swift +++ b/Networking/NetworkingTests/Mapper/OrderStatsMapperV4Tests.swift @@ -218,6 +218,49 @@ final class OrderStatsV4MapperTests: XCTestCase { XCTAssertNil(nonZeroYearTotals.totalProducts) XCTAssertEqual(nonZeroYearTotals.averageOrderValue, 266) } + + /// Verifies that all of the yearly unit OrderStatsV4 fields are parsed correctly + /// if the response contains no data envelope. + /// + func test_yearly_unit_stat_fields_are_properly_parsed_without_data_envelope() { + guard let yearlyStats = mapOrderStatsWithYearlyUnitResponseWithoutDataEnvelope() else { + XCTFail() + return + } + + XCTAssertEqual(yearlyStats.siteID, Constants.siteID) + XCTAssertEqual(yearlyStats.granularity, .yearly) + + XCTAssertEqual(yearlyStats.totals.totalOrders, 3) + XCTAssertEqual(yearlyStats.totals.totalItemsSold, 5) + XCTAssertEqual(yearlyStats.totals.grossRevenue, 800) + XCTAssertEqual(yearlyStats.totals.totalCoupons, 0) + XCTAssertEqual(yearlyStats.totals.couponDiscount, 0) + XCTAssertEqual(yearlyStats.totals.refunds, 0) + XCTAssertEqual(yearlyStats.totals.taxes, 0) + XCTAssertEqual(yearlyStats.totals.shipping, 0) + XCTAssertEqual(yearlyStats.totals.netRevenue, 800) + XCTAssertEqual(yearlyStats.totals.totalProducts, 2) + XCTAssertEqual(yearlyStats.totals.averageOrderValue, 266) + + XCTAssertEqual(yearlyStats.intervals.count, 1) + + let nonZeroYear = yearlyStats.intervals[0] + let nonZeroYearTotals = nonZeroYear.subtotals + + XCTAssertEqual(nonZeroYear.interval, "2019") + + XCTAssertEqual(nonZeroYearTotals.totalOrders, 3) + XCTAssertEqual(nonZeroYearTotals.grossRevenue, 800) + XCTAssertEqual(nonZeroYearTotals.totalCoupons, 0) + XCTAssertEqual(nonZeroYearTotals.couponDiscount, 0) + XCTAssertEqual(nonZeroYearTotals.refunds, 0) + XCTAssertEqual(nonZeroYearTotals.taxes, 0) + XCTAssertEqual(nonZeroYearTotals.shipping, 0) + XCTAssertEqual(nonZeroYearTotals.netRevenue, 800) + XCTAssertNil(nonZeroYearTotals.totalProducts) + XCTAssertEqual(nonZeroYearTotals.averageOrderValue, 266) + } } private extension OrderStatsV4MapperTests { @@ -251,6 +294,12 @@ private extension OrderStatsV4MapperTests { return mapStatItems(from: "order-stats-v4-year", granularity: .yearly) } + /// Returns the OrderStatsV4Mapper output upon receiving `order-stats-v4-year-without-data` + /// + func mapOrderStatsWithYearlyUnitResponseWithoutDataEnvelope() -> OrderStatsV4? { + return mapStatItems(from: "order-stats-v4-year-without-data", granularity: .yearly) + } + /// Returns the OrderStatsV4Mapper output upon receiving `filename` (Data Encoded) /// func mapStatItems(from filename: String, granularity: StatsGranularityV4) -> OrderStatsV4? {