Skip to content

Commit c402f55

Browse files
authored
Merge pull request #3871 from woocommerce/issue/2970-networking-and-yosemite-layers-shipping-labels-packages
Shipping Labels: endpoint to fetch list of default and custom packages for a store
2 parents dc3b8ea + 80c5936 commit c402f55

File tree

8 files changed

+186
-5
lines changed

8 files changed

+186
-5
lines changed

Networking/Networking/Model/ShippingLabel/Packages/Custom package/ShippingLabelCustomPackage.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,11 @@ public struct ShippingLabelCustomPackage: Equatable, GeneratedFakeable {
1616
/// Will be a string formatted like this: `2 x 3 x 4`
1717
public let dimensions: String
1818

19-
public let boxWeight: Int
19+
public let boxWeight: Double
2020

21-
public let maxWeight: Int
21+
public let maxWeight: Double
2222

23-
public init(isUserDefined: Bool, title: String, isLetter: Bool, dimensions: String, boxWeight: Int, maxWeight: Int) {
23+
public init(isUserDefined: Bool, title: String, isLetter: Bool, dimensions: String, boxWeight: Double, maxWeight: Double) {
2424
self.isUserDefined = isUserDefined
2525
self.title = title
2626
self.isLetter = isLetter
@@ -39,8 +39,8 @@ extension ShippingLabelCustomPackage: Decodable {
3939
let title = try container.decode(String.self, forKey: .title)
4040
let isLetter = try container.decodeIfPresent(Bool.self, forKey: .isLetter) ?? false
4141
let dimensions = try container.decode(String.self, forKey: .innerDimensions)
42-
let boxWeight = try container.decode(Int.self, forKey: .boxWeight)
43-
let maxWeight = try container.decode(Int.self, forKey: .maxWeight)
42+
let boxWeight = try container.decode(Double.self, forKey: .boxWeight)
43+
let maxWeight = try container.decode(Double.self, forKey: .maxWeight)
4444

4545
self.init(isUserDefined: isUserDefined, title: title, isLetter: isLetter, dimensions: dimensions, boxWeight: boxWeight, maxWeight: maxWeight)
4646
}

Networking/Networking/Remote/ShippingLabelRemote.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ public protocol ShippingLabelRemoteProtocol {
1414
func addressValidation(siteID: Int64,
1515
address: ShippingLabelAddressVerification,
1616
completion: @escaping (Result<ShippingLabelAddressValidationResponse, Error>) -> Void)
17+
func packagesDetails(siteID: Int64,
18+
completion: @escaping (Result<ShippingLabelPackagesResponse, Error>) -> Void)
1719
}
1820

1921
/// Shipping Labels Remote Endpoints.
@@ -84,13 +86,26 @@ public final class ShippingLabelRemote: Remote, ShippingLabelRemoteProtocol {
8486
completion(.failure(error))
8587
}
8688
}
89+
90+
/// Requests all the details for the packages (custom and predefined).
91+
/// - Parameters:
92+
/// - siteID: Remote ID of the site that owns the shipping label.
93+
/// - completion: Closure to be executed upon completion.
94+
public func packagesDetails(siteID: Int64,
95+
completion: @escaping (Result<ShippingLabelPackagesResponse, Error>) -> Void) {
96+
let path = Path.packages
97+
let request = JetpackRequest(wooApiVersion: .wcConnectV1, method: .get, siteID: siteID, path: path, parameters: nil)
98+
let mapper = ShippingLabelPackagesMapper()
99+
enqueue(request, mapper: mapper, completion: completion)
100+
}
87101
}
88102

89103
// MARK: Constant
90104
private extension ShippingLabelRemote {
91105
enum Path {
92106
static let shippingLabels = "label"
93107
static let normalizeAddress = "normalize-address"
108+
static let packages = "packages"
94109
}
95110

96111
enum ParameterKey {

Networking/NetworkingTests/Remote/ShippingLabelRemoteTests.swift

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,38 @@ final class ShippingLabelRemoteTests: XCTestCase {
139139
XCTAssertEqual(errors.addressError, "House number is missing")
140140
XCTAssertEqual(errors.generalError, "Address not found")
141141
}
142+
143+
func test_packagesDetails_returns_packages_on_success() throws {
144+
// Given
145+
let remote = ShippingLabelRemote(network: network)
146+
network.simulateResponse(requestUrlSuffix: "packages", filename: "shipping-label-packages-success")
147+
148+
// When
149+
let result: Result<ShippingLabelPackagesResponse, Error> = waitFor { promise in
150+
remote.packagesDetails(siteID: self.sampleSiteID) { result in
151+
promise(result)
152+
}
153+
}
154+
155+
// Then
156+
XCTAssertNotNil(try result.get())
157+
}
158+
159+
func test_packagesDetails_returns_errors_on_failure() throws {
160+
// Given
161+
let remote = ShippingLabelRemote(network: network)
162+
network.simulateResponse(requestUrlSuffix: "packages", filename: "generic_error")
163+
164+
// When
165+
let result: Result<ShippingLabelPackagesResponse, Error> = waitFor { promise in
166+
remote.packagesDetails(siteID: self.sampleSiteID) { result in
167+
promise(result)
168+
}
169+
}
170+
171+
// Then
172+
XCTAssertNotNil(result.failure)
173+
}
142174
}
143175

144176
private extension ShippingLabelRemoteTests {

Yosemite/Yosemite/Actions/ShippingLabelAction.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,11 @@ public enum ShippingLabelAction: Action {
2727
address: ShippingLabelAddressVerification,
2828
completion: (Result<ShippingLabelAddressValidationResponse, Error>) -> Void)
2929

30+
/// Requests all the details for the packages (custom and predefined).
31+
///
32+
case packagesDetails(siteID: Int64,
33+
completion: (Result<ShippingLabelPackagesResponse, Error>) -> Void)
34+
3035
/// Checks whether an order is eligible for shipping label creation.
3136
///
3237
case checkCreationEligibility(siteID: Int64, orderID: Int64, isFeatureFlagEnabled: Bool, onCompletion: (_ isEligible: Bool) -> Void)

Yosemite/Yosemite/Model/Model.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ public typealias ShipmentTrackingProviderGroup = Networking.ShipmentTrackingProv
7272
public typealias ShippingLabel = Networking.ShippingLabel
7373
public typealias ShippingLabelAddress = Networking.ShippingLabelAddress
7474
public typealias ShippingLabelAddressVerification = Networking.ShippingLabelAddressVerification
75+
public typealias ShippingLabelPackagesResponse = Networking.ShippingLabelPackagesResponse
7576
public typealias ShipType = Networking.ShippingLabelAddressVerification.ShipType
7677
public typealias ShippingLabelAddressValidationResponse = Networking.ShippingLabelAddressValidationResponse
7778
public typealias ShippingLabelAddressValidationError = Networking.ShippingLabelAddressValidationError

Yosemite/Yosemite/Stores/ShippingLabelStore.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ public final class ShippingLabelStore: Store {
4747
loadShippingLabelSettings(shippingLabel: shippingLabel, completion: completion)
4848
case .validateAddress(let siteID, let address, let completion):
4949
validateAddress(siteID: siteID, address: address, completion: completion)
50+
case .packagesDetails(let siteID, let completion):
51+
packagesDetails(siteID: siteID, completion: completion)
5052
case .checkCreationEligibility(let siteID, let orderID, let isFeatureFlagEnabled, let onCompletion):
5153
checkCreationEligibility(siteID: siteID, orderID: orderID, isFeatureFlagEnabled: isFeatureFlagEnabled, onCompletion: onCompletion)
5254
}
@@ -107,6 +109,11 @@ private extension ShippingLabelStore {
107109
remote.addressValidation(siteID: siteID, address: address, completion: completion)
108110
}
109111

112+
func packagesDetails(siteID: Int64,
113+
completion: @escaping (Result<ShippingLabelPackagesResponse, Error>) -> Void) {
114+
remote.packagesDetails(siteID: siteID, completion: completion)
115+
}
116+
110117
func checkCreationEligibility(siteID: Int64, orderID: Int64, isFeatureFlagEnabled: Bool, onCompletion: @escaping (_ isEligible: Bool) -> Void) {
111118
// TODO-2971: implement shipping label creation eligibility check, hopefully with the new `/creation_eligibility` endpoint.
112119
onCompletion(isFeatureFlagEnabled)

Yosemite/YosemiteTests/Mocks/Networking/Remote/MockShippingLabelRemote.swift

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ final class MockShippingLabelRemote {
2727
let siteID: Int64
2828
}
2929

30+
private struct PackagesDetailsResultKey: Hashable {
31+
let siteID: Int64
32+
}
33+
3034
/// The results to return based on the given arguments in `loadShippingLabels`
3135
private var loadAllResults = [LoadAllResultKey: Result<OrderShippingLabelListResponse, Error>]()
3236

@@ -39,6 +43,9 @@ final class MockShippingLabelRemote {
3943
/// The results to return based on the given arguments in `addressValidation`
4044
private var addressValidationResults = [AddressValidationResultKey: Result<ShippingLabelAddressValidationResponse, Error>]()
4145

46+
/// The results to return based on the given arguments in `packagesDetails`
47+
private var packagesDetailsResults = [PackagesDetailsResultKey: Result<ShippingLabelPackagesResponse, Error>]()
48+
4249
/// Set the value passed to the `completion` block if `loadShippingLabels` is called.
4350
func whenLoadingShippingLabels(siteID: Int64,
4451
orderID: Int64,
@@ -71,6 +78,13 @@ final class MockShippingLabelRemote {
7178
let key = AddressValidationResultKey(siteID: siteID)
7279
addressValidationResults[key] = result
7380
}
81+
82+
/// Set the value passed to the `completion` block if `packagesDetails` is called.
83+
func whenPackagesDetails(siteID: Int64,
84+
thenReturn result: Result<ShippingLabelPackagesResponse, Error>) {
85+
let key = PackagesDetailsResultKey(siteID: siteID)
86+
packagesDetailsResults[key] = result
87+
}
7488
}
7589

7690
// MARK: - ShippingLabelRemoteProtocol
@@ -130,4 +144,17 @@ extension MockShippingLabelRemote: ShippingLabelRemoteProtocol {
130144
}
131145
}
132146
}
147+
148+
func packagesDetails(siteID: Int64, completion: @escaping (Result<ShippingLabelPackagesResponse, Error>) -> Void) {
149+
DispatchQueue.main.async { [weak self] in
150+
guard let self = self else { return }
151+
152+
let key = PackagesDetailsResultKey(siteID: siteID)
153+
if let result = self.packagesDetailsResults[key] {
154+
completion(result)
155+
} else {
156+
XCTFail("\(String(describing: self)) Could not find Result for \(key)")
157+
}
158+
}
159+
}
133160
}

Yosemite/YosemiteTests/Stores/ShippingLabelStoreTests.swift

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,50 @@ final class ShippingLabelStoreTests: XCTestCase {
397397
XCTAssertEqual(error as? NetworkError, expectedError)
398398
}
399399

400+
// MARK: `packagesDetails`
401+
402+
func test_packagesDetails_returns_ShippingLabelPackagesResponse_on_success() throws {
403+
// Given
404+
let remote = MockShippingLabelRemote()
405+
let expectedResult = sampleShippingLabelPackagesResponse()
406+
remote.whenPackagesDetails(siteID: sampleSiteID,
407+
thenReturn: .success(expectedResult))
408+
let store = ShippingLabelStore(dispatcher: dispatcher, storageManager: storageManager, network: network, remote: remote)
409+
410+
// When
411+
let result: Result<ShippingLabelPackagesResponse, Error> = waitFor { promise in
412+
let action = ShippingLabelAction.packagesDetails(siteID: self.sampleSiteID) { result in
413+
promise(result)
414+
}
415+
store.onAction(action)
416+
}
417+
418+
// Then
419+
let printData = try XCTUnwrap(result.get())
420+
XCTAssertEqual(printData, expectedResult)
421+
}
422+
423+
func test_packagesDetails_returns_error_on_failure() throws {
424+
// Given
425+
let remote = MockShippingLabelRemote()
426+
let expectedError = NetworkError.notFound
427+
remote.whenPackagesDetails(siteID: sampleSiteID,
428+
thenReturn: .failure(expectedError))
429+
let store = ShippingLabelStore(dispatcher: dispatcher, storageManager: storageManager, network: network, remote: remote)
430+
431+
// When
432+
let result: Result<ShippingLabelPackagesResponse, Error> = waitFor { promise in
433+
let action = ShippingLabelAction.packagesDetails(siteID: self.sampleSiteID) { result in
434+
promise(result)
435+
}
436+
store.onAction(action)
437+
}
438+
439+
// Then
440+
let error = try XCTUnwrap(result.failure)
441+
XCTAssertEqual(error as? NetworkError, expectedError)
442+
}
443+
400444
// MARK: `checkCreationEligibility`
401445

402446
func test_checkCreationEligibility_returns_feature_flag_value() throws {
@@ -455,4 +499,54 @@ private extension ShippingLabelStoreTests {
455499
city: "SAN FRANCISCO",
456500
postcode: "94110-4929")
457501
}
502+
503+
func sampleShippingLabelPackagesResponse() -> Yosemite.ShippingLabelPackagesResponse {
504+
return ShippingLabelPackagesResponse(storeOptions: sampleShippingLabelStoreOptions(),
505+
customPackages: sampleShippingLabelCustomPackages(),
506+
predefinedOptions: sampleShippingLabelPredefinedOptions())
507+
508+
}
509+
510+
func sampleShippingLabelStoreOptions() -> ShippingLabelStoreOptions {
511+
return ShippingLabelStoreOptions(currencySymbol: "$", dimensionUnit: "cm", weightUnit: "kg", originCountry: "US")
512+
}
513+
514+
func sampleShippingLabelCustomPackages() -> [ShippingLabelCustomPackage] {
515+
let customPackage1 = ShippingLabelCustomPackage(isUserDefined: true,
516+
title: "Krabica",
517+
isLetter: false,
518+
dimensions: "1 x 2 x 3",
519+
boxWeight: 1,
520+
maxWeight: 0)
521+
let customPackage2 = ShippingLabelCustomPackage(isUserDefined: true,
522+
title: "Obalka",
523+
isLetter: true,
524+
dimensions: "2 x 3 x 4",
525+
boxWeight: 5,
526+
maxWeight: 0)
527+
528+
return [customPackage1, customPackage2]
529+
}
530+
531+
func sampleShippingLabelPredefinedOptions() -> [ShippingLabelPredefinedOption] {
532+
let predefinedPackages1 = [ShippingLabelPredefinedPackage(id: "small_flat_box",
533+
title: "Small Flat Rate Box",
534+
isLetter: false,
535+
dimensions: "21.91 x 13.65 x 4.13"),
536+
ShippingLabelPredefinedPackage(id: "medium_flat_box_top",
537+
title: "Medium Flat Rate Box 1, Top Loading",
538+
isLetter: false,
539+
dimensions: "28.57 x 22.22 x 15.24")]
540+
let predefinedOption1 = ShippingLabelPredefinedOption(title: "USPS Priority Mail Flat Rate Boxes",
541+
predefinedPackages: predefinedPackages1)
542+
543+
let predefinedPackages2 = [ShippingLabelPredefinedPackage(id: "LargePaddedPouch",
544+
title: "Large Padded Pouch",
545+
isLetter: true,
546+
dimensions: "30.22 x 35.56 x 2.54")]
547+
let predefinedOption2 = ShippingLabelPredefinedOption(title: "DHL Express",
548+
predefinedPackages: predefinedPackages2)
549+
550+
return [predefinedOption1, predefinedOption2]
551+
}
458552
}

0 commit comments

Comments
 (0)