Skip to content

Commit 24ed98a

Browse files
authored
Update OrdersRemote with async/throws network methods to parse orders response in a background thread (#15982)
2 parents c9e80a2 + 8377aba commit 24ed98a

File tree

5 files changed

+87
-121
lines changed

5 files changed

+87
-121
lines changed

Modules/Sources/NetworkingCore/Remote/OrdersRemote.swift

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ public class OrdersRemote: Remote {
2525
/// - createdVia: If given, limit response to orders created via the specified source (e.g. "pos-rest-api" for Point of Sale).
2626
/// - pageNumber: Number of page that should be retrieved.
2727
/// - pageSize: Number of Orders to be retrieved per page.
28-
/// - completion: Closure to be executed upon completion.
28+
/// - Returns: Array of orders.
29+
/// - Throws: Network or parsing errors.
2930
///
3031
public func loadAllOrders(for siteID: Int64,
3132
statuses: [String]? = nil,
@@ -36,8 +37,7 @@ public class OrdersRemote: Remote {
3637
productID: Int64? = nil,
3738
createdVia: String? = nil,
3839
pageNumber: Int = Defaults.pageNumber,
39-
pageSize: Int = Defaults.pageSize,
40-
completion: @escaping (Result<[Order], Error>) -> Void) {
40+
pageSize: Int = Defaults.pageSize) async throws -> [Order] {
4141
let utcDateFormatter = DateFormatter.Defaults.iso8601
4242

4343
let statusesString: String? = statuses?.isEmpty == true ? Defaults.statusAny : statuses?.joined(separator: ",")
@@ -84,7 +84,7 @@ public class OrdersRemote: Remote {
8484
availableAsRESTRequest: true)
8585
let mapper = OrderListMapper(siteID: siteID)
8686

87-
enqueue(request, mapper: mapper, completion: completion)
87+
return try await enqueue(request, mapper: mapper)
8888
}
8989

9090
/// Retrieves a specific `Order`
@@ -138,13 +138,13 @@ public class OrdersRemote: Remote {
138138
/// - keyword: Search string that should be matched by the orders.
139139
/// - pageNumber: Number of page that should be retrieved.
140140
/// - pageSize: Number of Orders to be retrieved per page.
141-
/// - completion: Closure to be executed upon completion.
141+
/// - Returns: Array of orders matching the search criteria.
142+
/// - Throws: Network or parsing errors.
142143
///
143144
public func searchOrders(for siteID: Int64,
144145
keyword: String,
145146
pageNumber: Int = Defaults.pageNumber,
146-
pageSize: Int = Defaults.pageSize,
147-
completion: @escaping ([Order]?, Error?) -> Void) {
147+
pageSize: Int = Defaults.pageSize) async throws -> [Order] {
148148
let parameters = [
149149
ParameterKeys.keyword: keyword,
150150
ParameterKeys.page: String(pageNumber),
@@ -162,7 +162,7 @@ public class OrdersRemote: Remote {
162162
availableAsRESTRequest: true)
163163
let mapper = OrderListMapper(siteID: siteID)
164164

165-
enqueue(request, mapper: mapper, completion: completion)
165+
return try await enqueue(request, mapper: mapper)
166166
}
167167

168168
/// Creates an order using the specified fields of a given order

Modules/Sources/Yosemite/Stores/OrderStore.swift

Lines changed: 40 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -113,14 +113,14 @@ private extension OrderStore {
113113
/// Searches all of the orders that contain a given Keyword.
114114
///
115115
func searchOrders(siteID: Int64, keyword: String, pageNumber: Int, pageSize: Int, onCompletion: @escaping (Error?) -> Void) {
116-
remote.searchOrders(for: siteID, keyword: keyword, pageNumber: pageNumber, pageSize: pageSize) { [weak self] (orders, error) in
117-
guard let orders = orders else {
116+
Task { @MainActor in
117+
do {
118+
let orders = try await remote.searchOrders(for: siteID, keyword: keyword, pageNumber: pageNumber, pageSize: pageSize)
119+
upsertSearchResultsInBackground(for: siteID, keyword: keyword, readOnlyOrders: orders) {
120+
onCompletion(nil)
121+
}
122+
} catch {
118123
onCompletion(error)
119-
return
120-
}
121-
122-
self?.upsertSearchResultsInBackground(for: siteID, keyword: keyword, readOnlyOrders: orders) {
123-
onCompletion(nil)
124124
}
125125
}
126126
}
@@ -155,18 +155,19 @@ private extension OrderStore {
155155

156156
let pageNumber = OrdersRemote.Defaults.pageNumber
157157
let startTime = Date()
158-
self.remote.loadAllOrders(for: siteID,
159-
statuses: statuses,
160-
after: after,
161-
before: before,
162-
modifiedAfter: modifiedAfter,
163-
customerID: customerID,
164-
productID: productID,
165-
createdVia: createdVia,
166-
pageNumber: pageNumber,
167-
pageSize: pageSize) { [weak self] result in
168-
switch result {
169-
case .success(let orders):
158+
Task { @MainActor [weak self] in
159+
do {
160+
let orders = try await self?.remote.loadAllOrders(for: siteID,
161+
statuses: statuses,
162+
after: after,
163+
before: before,
164+
modifiedAfter: modifiedAfter,
165+
customerID: customerID,
166+
productID: productID,
167+
createdVia: createdVia,
168+
pageNumber: pageNumber,
169+
pageSize: pageSize) ?? []
170+
let result: Result<[Order], Error> = .success(orders)
170171
switch writeStrategy {
171172
case .doNotSave:
172173
onCompletion(Date().timeIntervalSince(startTime), result)
@@ -176,7 +177,8 @@ private extension OrderStore {
176177
onCompletion(Date().timeIntervalSince(startTime), result)
177178
}
178179
}
179-
case .failure:
180+
} catch {
181+
let result: Result<[Order], Error> = .failure(error)
180182
onCompletion(Date().timeIntervalSince(startTime), result)
181183
}
182184
}
@@ -196,22 +198,22 @@ private extension OrderStore {
196198
pageSize: Int,
197199
onCompletion: @escaping (TimeInterval, Error?) -> Void) {
198200
let startTime = Date()
199-
remote.loadAllOrders(for: siteID,
200-
statuses: statuses,
201-
after: after,
202-
before: before,
203-
modifiedAfter: modifiedAfter,
204-
customerID: customerID,
205-
productID: productID,
206-
createdVia: createdVia,
207-
pageNumber: pageNumber,
208-
pageSize: pageSize) { [weak self] result in
209-
switch result {
210-
case .success(let orders):
211-
self?.upsertStoredOrdersInBackground(readOnlyOrders: orders) {
201+
Task { @MainActor in
202+
do {
203+
let orders = try await remote.loadAllOrders(for: siteID,
204+
statuses: statuses,
205+
after: after,
206+
before: before,
207+
modifiedAfter: modifiedAfter,
208+
customerID: customerID,
209+
productID: productID,
210+
createdVia: createdVia,
211+
pageNumber: pageNumber,
212+
pageSize: pageSize)
213+
upsertStoredOrdersInBackground(readOnlyOrders: orders) {
212214
onCompletion(Date().timeIntervalSince(startTime), nil)
213215
}
214-
case .failure(let error):
216+
} catch {
215217
onCompletion(Date().timeIntervalSince(startTime), error)
216218
}
217219
}
@@ -228,11 +230,11 @@ private extension OrderStore {
228230
}
229231

230232
// If there are no locally stored orders, then check remote.
231-
remote.loadAllOrders(for: siteID, pageNumber: Default.firstPageNumber, pageSize: 1) { result in
232-
switch result {
233-
case .success(let orders):
233+
Task { @MainActor in
234+
do {
235+
let orders = try await remote.loadAllOrders(for: siteID, pageNumber: Default.firstPageNumber, pageSize: 1)
234236
onCompletion(.success(orders.isEmpty == false))
235-
case .failure(let error):
237+
} catch {
236238
onCompletion(.failure(error))
237239
}
238240
}

Modules/Tests/NetworkingTests/Remote/OrdersRemoteTests.swift

Lines changed: 30 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -60,57 +60,42 @@ final class OrdersRemoteTests: XCTestCase {
6060

6161
/// Verifies that loadAllOrders properly parses the `orders-load-all` sample response.
6262
///
63-
func testLoadAllOrdersProperlyReturnsParsedOrders() throws {
63+
func test_loadAllOrders_properly_returns_parsed_orders() async throws {
6464
// Given
6565
let remote = OrdersRemote(network: network)
6666

6767
network.simulateResponse(requestUrlSuffix: "orders", filename: "orders-load-all")
6868

6969
// When
70-
var result: Result<[Order], Error>?
71-
waitForExpectation { expectation in
72-
remote.loadAllOrders(for: sampleSiteID) { aResult in
73-
result = aResult
74-
expectation.fulfill()
75-
}
76-
}
70+
let orders = try await remote.loadAllOrders(for: sampleSiteID)
7771

7872
// Then
79-
let orders = try XCTUnwrap(result?.get())
8073
XCTAssert(orders.count == 4)
8174
}
8275

8376
/// Verifies that loadAllOrders properly relays Networking Layer errors.
8477
///
85-
func testLoadAllOrdersProperlyRelaysNetworkingErrors() throws {
78+
func test_loadAllOrders_properly_relays_networking_errors() async throws {
8679
// Given
8780
let remote = OrdersRemote(network: network)
8881

89-
// When
90-
var result: Result<[Order], Error>?
91-
waitForExpectation { expectation in
92-
remote.loadAllOrders(for: sampleSiteID) { aResult in
93-
result = aResult
94-
expectation.fulfill()
95-
}
82+
// When & Then
83+
do {
84+
_ = try await remote.loadAllOrders(for: sampleSiteID)
85+
XCTFail("Expected error to be thrown")
86+
} catch {
87+
XCTAssertEqual(error as? NetworkError, .notFound(response: nil))
9688
}
97-
98-
// Then
99-
XCTAssertTrue(try XCTUnwrap(result).isFailure)
10089
}
10190

102-
func test_loadAllOrders_includes_modifiedAfter_parameter_when_provided() {
91+
func test_loadAllOrders_includes_modifiedAfter_parameter_when_provided() async throws {
10392
// Given
10493
let remote = OrdersRemote(network: network)
10594
let modifiedAfter = Date()
10695
network.simulateResponse(requestUrlSuffix: "orders", filename: "orders-load-all")
10796

10897
// When
109-
_ = waitFor { promise in
110-
remote.loadAllOrders(for: self.sampleSiteID, modifiedAfter: modifiedAfter) { result in
111-
promise(result)
112-
}
113-
}
98+
_ = try await remote.loadAllOrders(for: sampleSiteID, modifiedAfter: modifiedAfter)
11499

115100
// Then
116101
guard let queryParameters = network.queryParameters else {
@@ -123,18 +108,14 @@ final class OrdersRemoteTests: XCTestCase {
123108
XCTAssertTrue(queryParameters.contains(expectedParam), "Expected to have param: \(expectedParam)")
124109
}
125110

126-
func test_loadAllOrders_includes_customer_parameter_when_provided() {
111+
func test_loadAllOrders_includes_customer_parameter_when_provided() async throws {
127112
// Given
128113
let remote = OrdersRemote(network: network)
129114
network.simulateResponse(requestUrlSuffix: "orders", filename: "orders-load-all")
130115
let expectedCustomerID: Int64 = 123
131116

132117
// When
133-
_ = waitFor { promise in
134-
remote.loadAllOrders(for: self.sampleSiteID, customerID: expectedCustomerID) { result in
135-
promise(result)
136-
}
137-
}
118+
_ = try await remote.loadAllOrders(for: sampleSiteID, customerID: expectedCustomerID)
138119

139120
// Then
140121
guard let queryParameters = network.queryParameters else {
@@ -146,18 +127,14 @@ final class OrdersRemoteTests: XCTestCase {
146127
XCTAssertTrue(queryParameters.contains(expectedParam), "Expected to have param: \(expectedParam)")
147128
}
148129

149-
func test_loadAllOrders_includes_product_parameter_when_provided() {
130+
func test_loadAllOrders_includes_product_parameter_when_provided() async throws {
150131
// Given
151132
let remote = OrdersRemote(network: network)
152133
network.simulateResponse(requestUrlSuffix: "orders", filename: "orders-load-all")
153134
let expectedProductID: Int64 = 13
154135

155136
// When
156-
_ = waitFor { promise in
157-
remote.loadAllOrders(for: self.sampleSiteID, productID: expectedProductID) { result in
158-
promise(result)
159-
}
160-
}
137+
_ = try await remote.loadAllOrders(for: sampleSiteID, productID: expectedProductID)
161138

162139
// Then
163140
guard let queryParameters = network.queryParameters else {
@@ -243,35 +220,33 @@ final class OrdersRemoteTests: XCTestCase {
243220

244221
/// Verifies that searchOrders properly parses the `orders-load-all` sample response.
245222
///
246-
func testSearchOrdersProperlyReturnsParsedOrders() {
223+
func test_searchOrders_properly_returns_parsed_orders() async throws {
224+
// Given
247225
let remote = OrdersRemote(network: network)
248-
let expectation = self.expectation(description: "Load All Orders")
249226

250227
network.simulateResponse(requestUrlSuffix: "orders", filename: "orders-load-all")
251228

252-
remote.searchOrders(for: sampleSiteID, keyword: String()) { (orders, error) in
253-
XCTAssertNil(error)
254-
XCTAssertNotNil(orders)
255-
XCTAssert(orders!.count == 4)
256-
expectation.fulfill()
257-
}
229+
// When
230+
let orders = try await remote.searchOrders(for: sampleSiteID, keyword: String())
258231

259-
wait(for: [expectation], timeout: Constants.expectationTimeout)
232+
// Then
233+
XCTAssert(orders.count == 4)
260234
}
261235

262236
/// Verifies that searchOrders properly relays Networking Layer errors.
263237
///
264-
func testSearchOrdersProperlyRelaysNetworkingErrors() {
238+
func test_searchOrders_properly_relays_networking_error() async throws {
239+
// Given
265240
let remote = OrdersRemote(network: network)
266-
let expectation = self.expectation(description: "Load All Orders")
267241

268-
remote.searchOrders(for: sampleSiteID, keyword: String()) { (orders, error) in
269-
XCTAssertNil(orders)
270-
XCTAssertNotNil(error)
271-
expectation.fulfill()
242+
do {
243+
// When
244+
_ = try await remote.searchOrders(for: sampleSiteID, keyword: String())
245+
XCTFail("Expected error to be thrown")
246+
} catch {
247+
// Then
248+
XCTAssertEqual(error as? NetworkError, .notFound(response: nil))
272249
}
273-
274-
wait(for: [expectation], timeout: Constants.expectationTimeout)
275250
}
276251

277252

WooCommerce/Classes/ViewRelated/Connectivity Tool/ConnectivityToolViewModel.swift

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -177,20 +177,13 @@ final class ConnectivityToolViewModel {
177177
/// Test fetching the site orders by actually fetching orders.
178178
///
179179
func testFetchingOrders() async -> ConnectivityToolCard.State {
180-
await withCheckedContinuation { continuation in
181-
orderRemote?.loadAllOrders(for: siteID) { [weak self] result in
182-
guard let self else { return }
183-
184-
switch result {
185-
case .success:
186-
DDLogInfo("Connectivity Tool: ✅ Site Orders")
187-
case .failure(let error):
188-
DDLogError("Connectivity Tool: ❌ Site Orders\n\(error)")
189-
}
190-
191-
let state = self.stateForSiteResult(result)
192-
continuation.resume(returning: state)
193-
}
180+
do {
181+
_ = try await orderRemote?.loadAllOrders(for: siteID)
182+
DDLogInfo("Connectivity Tool: ✅ Site Orders")
183+
return stateForSiteResult(Result<[Order], Error>.success([]))
184+
} catch {
185+
DDLogError("Connectivity Tool: ❌ Site Orders\n\(error)")
186+
return stateForSiteResult(Result<[Order], Error>.failure(error))
194187
}
195188
}
196189

WooCommerce/Woo Watch App/Orders/OrdersDataService.swift

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,9 @@ final class OrdersDataService {
1717
ordersRemote = OrdersRemote(network: network)
1818
}
1919

20-
/// Async wrapper that fetches orders for a store ID.
20+
/// Fetches orders for a store ID.
2121
///
2222
func loadAllOrders(for storeID: Int64, pageNumber: Int, pageSize: Int) async throws -> [Order] {
23-
try await withCheckedThrowingContinuation { continuation in
24-
ordersRemote.loadAllOrders(for: storeID, pageNumber: pageNumber, pageSize: pageSize) { result in
25-
continuation.resume(with: result)
26-
}
27-
}
23+
try await ordersRemote.loadAllOrders(for: storeID, pageNumber: pageNumber, pageSize: pageSize)
2824
}
2925
}

0 commit comments

Comments
 (0)