Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Shipping Labels] Update shipment endpoint - Networking layer #15445

Merged
merged 9 commits into from
Mar 28, 2025
21 changes: 21 additions & 0 deletions Fakes/Fakes/Networking.generated.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2988,6 +2988,27 @@ extension Networking.WooShippingShipment {
)
}
}
extension Networking.WooShippingUpdateShipment {
/// Returns a "ready to use" type filled with fake values.
///
public static func fake() -> Networking.WooShippingUpdateShipment {
.init(
shipmentIdsToUpdate: .fake(),
shipments: .fake()
)
}
}
extension Networking.WooShippingUpdateShipmentResponse {
/// Returns a "ready to use" type filled with fake values.
///
public static func fake() -> Networking.WooShippingUpdateShipmentResponse {
.init(
siteID: .fake(),
orderID: .fake(),
shipments: .fake()
)
}
}
extension Networking.WordPressMedia {
/// Returns a "ready to use" type filled with fake values.
///
Expand Down
24 changes: 24 additions & 0 deletions Networking/Networking.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -1291,6 +1291,12 @@
EEA658482966CBAD00112DF0 /* EntityIDMapperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEA658472966CBAD00112DF0 /* EntityIDMapperTests.swift */; };
EEA6584C2966CC4800112DF0 /* product-id-only-without-data.json in Resources */ = {isa = PBXBuildFile; fileRef = EEA6584B2966CC4800112DF0 /* product-id-only-without-data.json */; };
EEAB476A2A851AFD00E55B25 /* site-upload-profiler-answers-success.json in Resources */ = {isa = PBXBuildFile; fileRef = EEAB47692A851AFD00E55B25 /* site-upload-profiler-answers-success.json */; };
EEBBDD842D943D2D008D6CE5 /* WooShippingUpdateShipment.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEBBDD832D943D25008D6CE5 /* WooShippingUpdateShipment.swift */; };
EEBBDD862D9440DD008D6CE5 /* WooShippingUpdateShipmentMapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEBBDD852D9440BD008D6CE5 /* WooShippingUpdateShipmentMapper.swift */; };
EEBBDD882D94419F008D6CE5 /* WooShippingUpdateShipmentResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEBBDD872D944194008D6CE5 /* WooShippingUpdateShipmentResponse.swift */; };
EEBBDD8B2D944380008D6CE5 /* shipping-label-update-shipment-without-data.json in Resources */ = {isa = PBXBuildFile; fileRef = EEBBDD8A2D944380008D6CE5 /* shipping-label-update-shipment-without-data.json */; };
EEBBDD8C2D944380008D6CE5 /* shipping-label-update-shipment.json in Resources */ = {isa = PBXBuildFile; fileRef = EEBBDD892D944380008D6CE5 /* shipping-label-update-shipment.json */; };
EEBBFEAC2D9532C6008D6CE5 /* WooShippingUpdateShipmentMapperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEBBFEAB2D9532C3008D6CE5 /* WooShippingUpdateShipmentMapperTests.swift */; };
EEC312C32AFDF79E004369F7 /* ProductSubscriptionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEC312C22AFDF79E004369F7 /* ProductSubscriptionTests.swift */; };
EEC312C52AFE01BC004369F7 /* ProductEncoderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEC312C42AFE01BC004369F7 /* ProductEncoderTests.swift */; };
EECB7EE8286555180028C888 /* media-update-product-id.json in Resources */ = {isa = PBXBuildFile; fileRef = EECB7EE7286555180028C888 /* media-update-product-id.json */; };
Expand Down Expand Up @@ -2528,6 +2534,12 @@
EEA658472966CBAD00112DF0 /* EntityIDMapperTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EntityIDMapperTests.swift; sourceTree = "<group>"; };
EEA6584B2966CC4800112DF0 /* product-id-only-without-data.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "product-id-only-without-data.json"; sourceTree = "<group>"; };
EEAB47692A851AFD00E55B25 /* site-upload-profiler-answers-success.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "site-upload-profiler-answers-success.json"; sourceTree = "<group>"; };
EEBBDD832D943D25008D6CE5 /* WooShippingUpdateShipment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WooShippingUpdateShipment.swift; sourceTree = "<group>"; };
EEBBDD852D9440BD008D6CE5 /* WooShippingUpdateShipmentMapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WooShippingUpdateShipmentMapper.swift; sourceTree = "<group>"; };
EEBBDD872D944194008D6CE5 /* WooShippingUpdateShipmentResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WooShippingUpdateShipmentResponse.swift; sourceTree = "<group>"; };
EEBBDD892D944380008D6CE5 /* shipping-label-update-shipment.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "shipping-label-update-shipment.json"; sourceTree = "<group>"; };
EEBBDD8A2D944380008D6CE5 /* shipping-label-update-shipment-without-data.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "shipping-label-update-shipment-without-data.json"; sourceTree = "<group>"; };
EEBBFEAB2D9532C3008D6CE5 /* WooShippingUpdateShipmentMapperTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WooShippingUpdateShipmentMapperTests.swift; sourceTree = "<group>"; };
EEC312C22AFDF79E004369F7 /* ProductSubscriptionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProductSubscriptionTests.swift; sourceTree = "<group>"; };
EEC312C42AFE01BC004369F7 /* ProductEncoderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProductEncoderTests.swift; sourceTree = "<group>"; };
EECB7EE7286555180028C888 /* media-update-product-id.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "media-update-product-id.json"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -3288,6 +3300,8 @@
DE66C55C2977C35A00DAA978 /* shipping-label-packages-success-without-data.json */,
EE74B1EB2D895FA4000E4C56 /* shipping-label-config-success.json */,
EE74B1EC2D895FA4000E4C56 /* shipping-label-config-success-without-data.json */,
EEBBDD892D944380008D6CE5 /* shipping-label-update-shipment.json */,
EEBBDD8A2D944380008D6CE5 /* shipping-label-update-shipment-without-data.json */,
DE66C55A2977C1A000DAA978 /* order-shipping-labels-without-data.json */,
DE66C558297799CF00DAA978 /* add-on-groups-without-data.json */,
DE66C5542976662700DAA978 /* just-in-time-message-list-without-data.json */,
Expand Down Expand Up @@ -3804,6 +3818,7 @@
EE105F402D671ED3005AB07F /* WooShippingDestinationAddressUpdateMapper.swift */,
CED9BCC42D3EBD0D00C063B8 /* WooShippingAddressValidationSuccessMapper.swift */,
EE1042AC2D65CE5E005AB07F /* WooShippingVerifyDestinationAddressMapper.swift */,
EEBBDD852D9440BD008D6CE5 /* WooShippingUpdateShipmentMapper.swift */,
CE90E99D2CEFCB100068D852 /* WooShippingStatusMapper.swift */,
EE7E80B72D84F18700E6FF5B /* WooShippingConfigMapper.swift */,
CE606D8E2BE39426001CB424 /* ShippingMethodMapper.swift */,
Expand Down Expand Up @@ -3982,6 +3997,7 @@
CC9A254926442D26005DE56E /* ShippingLabelCreationEligibilityMapperTests.swift */,
CC07866426790B1100BA9AC1 /* ShippingLabelPurchaseMapperTests.swift */,
EE74B1E92D895F00000E4C56 /* WooShippingConfigMapperTests.swift */,
EEBBFEAB2D9532C3008D6CE5 /* WooShippingUpdateShipmentMapperTests.swift */,
CC0786C8267BB32800BA9AC1 /* ShippingLabelStatusMapperTests.swift */,
CE606D922BE39889001CB424 /* ShippingMethodListMapperTests.swift */,
02C254D22563992900A04423 /* OrderShippingLabelListMapperTests.swift */,
Expand Down Expand Up @@ -4228,8 +4244,10 @@
EE7E80BB2D84F4E900E6FF5B /* Shipments */ = {
isa = PBXGroup;
children = (
EEBBDD832D943D25008D6CE5 /* WooShippingUpdateShipment.swift */,
EE7E80BC2D84F54800E6FF5B /* WooShippingShipment.swift */,
EE7E80B92D84F41500E6FF5B /* WooShippingConfigResponse.swift */,
EEBBDD872D944194008D6CE5 /* WooShippingUpdateShipmentResponse.swift */,
);
path = Shipments;
sourceTree = "<group>";
Expand Down Expand Up @@ -4759,6 +4777,8 @@
02698CFC24C1B0CE005337C4 /* product-variations-load-all-first-on-sale-empty-sale-price.json in Resources */,
31A451D427863A2E00FE81AA /* stripe-account-rejected-listed.json in Resources */,
74A1D265211898F000931DFA /* site-visits-month.json in Resources */,
EEBBDD8B2D944380008D6CE5 /* shipping-label-update-shipment-without-data.json in Resources */,
EEBBDD8C2D944380008D6CE5 /* shipping-label-update-shipment.json in Resources */,
31799AFC2705189200D78179 /* wcpay-location.json in Resources */,
028CB71E2902589E00331C09 /* create-account-error-email-exists.json in Resources */,
74159623224D2C86003C21CF /* settings-product.json in Resources */,
Expand Down Expand Up @@ -5120,6 +5140,7 @@
74ABA1CF213F1D1600FFAD30 /* TopEarnerStatsItem.swift in Sources */,
FE28F6E6268429B6004465C7 /* UserRemote.swift in Sources */,
02AD47702A6EB71100E652D6 /* URLRequestConvertible+Path.swift in Sources */,
EEBBDD882D94419F008D6CE5 /* WooShippingUpdateShipmentResponse.swift in Sources */,
68BFF8FE2B676A9F00B15FF2 /* Receipt.swift in Sources */,
7452387421124B7700A973CD /* AnyDecodable.swift in Sources */,
DE4D23B829B5F909003A4B5D /* Announcement.swift in Sources */,
Expand Down Expand Up @@ -5285,6 +5306,7 @@
CC75108B29EFF1A90035FBA4 /* SubscriptionStatus.swift in Sources */,
CC07865F267799EE00BA9AC1 /* ShippingLabelPurchase.swift in Sources */,
45A4B84E25D2E11300776FB4 /* ShippingLabelAddressValidationSuccessMapper.swift in Sources */,
EEBBDD842D943D2D008D6CE5 /* WooShippingUpdateShipment.swift in Sources */,
CC851D0625E51ADF00249E9C /* Decimal+Extensions.swift in Sources */,
45D1CF4923BACA6500945A36 /* ProductTaxStatus.swift in Sources */,
EE57C111297927C600BC31E7 /* ApplicationPasswordNameAndUUIDMapper.swift in Sources */,
Expand Down Expand Up @@ -5345,6 +5367,7 @@
CED9BCC52D3EBD0D00C063B8 /* WooShippingAddressValidationSuccessMapper.swift in Sources */,
B9CFF6522AB2118900C2F616 /* TaxRateMapper.swift in Sources */,
DE2095BF279583A100171F1C /* CouponReportListMapper.swift in Sources */,
EEBBDD862D9440DD008D6CE5 /* WooShippingUpdateShipmentMapper.swift in Sources */,
B557DA0320975500005962F4 /* Remote.swift in Sources */,
B53EF53C21814900003E146F /* SuccessResultMapper.swift in Sources */,
D8EDFE2625EE8A60003D2213 /* ReaderConnectionTokenMapper.swift in Sources */,
Expand Down Expand Up @@ -5749,6 +5772,7 @@
CC851D1425E52AB500249E9C /* Decimal+ExtensionsTests.swift in Sources */,
EE76762F2962B85E000066FA /* RequestProcessorTests.swift in Sources */,
B554FA8B2180B1D500C54DFF /* NotificationsRemoteTests.swift in Sources */,
EEBBFEAC2D9532C6008D6CE5 /* WooShippingUpdateShipmentMapperTests.swift in Sources */,
B518662A20A09C6F00037A38 /* OrdersRemoteTests.swift in Sources */,
02EF166E292F0C5800D90AD6 /* PaymentRemoteTests.swift in Sources */,
B5969E1520A47F99005E9DF1 /* RemoteTests.swift in Sources */,
Expand Down
40 changes: 40 additions & 0 deletions Networking/Networking/Mapper/WooShippingUpdateShipmentMapper.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import Foundation

/// Mapper: Shipping Label Update Shipments Response from WooCommerce Shipping extension
///
struct WooShippingUpdateShipmentMapper: Mapper {
/// Site Identifier associated to the order that will be parsed.
///
/// We're injecting this field via `JSONDecoder.userInfo` because SiteID is not returned by the update shipment endpoint.
///
let siteID: Int64

/// Order ID associated to the shipping labels that will be parsed.
///
/// We're injecting this field via `JSONDecoder.userInfo` because OrderID is not returned by the update shipment endpoint.
///
let orderID: Int64

/// (Attempts) to convert a dictionary into WooShippingUpdateShipmentResponse.
///
func map(response: Data) throws -> WooShippingUpdateShipmentResponse {
let decoder = JSONDecoder()
decoder.userInfo = [
.siteID: siteID,
.orderID: orderID
]
if hasDataEnvelope(in: response) {
return try decoder.decode(WooShippingUpdateShipmentResponseEnvelope.self, from: response).data
} else {
return try decoder.decode(WooShippingUpdateShipmentResponse.self, from: response)
}
}
}

private struct WooShippingUpdateShipmentResponseEnvelope: Decodable {
let data: WooShippingUpdateShipmentResponse

private enum CodingKeys: String, CodingKey {
case data = "data"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4437,6 +4437,39 @@ extension Networking.WooShippingShipment {
}
}

extension Networking.WooShippingUpdateShipment {
public func copy(
shipmentIdsToUpdate: CopiableProp<[String]> = .copy,
shipments: CopiableProp<[String: [WooShippingShipment]]> = .copy
) -> Networking.WooShippingUpdateShipment {
let shipmentIdsToUpdate = shipmentIdsToUpdate ?? self.shipmentIdsToUpdate
let shipments = shipments ?? self.shipments

return Networking.WooShippingUpdateShipment(
shipmentIdsToUpdate: shipmentIdsToUpdate,
shipments: shipments
)
}
}

extension Networking.WooShippingUpdateShipmentResponse {
public func copy(
siteID: CopiableProp<Int64> = .copy,
orderID: CopiableProp<Int64> = .copy,
shipments: CopiableProp<[String: [WooShippingShipment]]> = .copy
) -> Networking.WooShippingUpdateShipmentResponse {
let siteID = siteID ?? self.siteID
let orderID = orderID ?? self.orderID
let shipments = shipments ?? self.shipments

return Networking.WooShippingUpdateShipmentResponse(
siteID: siteID,
orderID: orderID,
shipments: shipments
)
}
}

extension Networking.WordPressMedia {
public func copy(
mediaID: CopiableProp<Int64> = .copy,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import Foundation
import Codegen

/// Model to update shipments on an order
///
public struct WooShippingUpdateShipment: Equatable, Encodable, GeneratedFakeable, GeneratedCopiable {
public let shipmentIdsToUpdate: [String]

public let shipments: [String: [WooShippingShipment]]

public init(shipmentIdsToUpdate: [String], shipments: [String: [WooShippingShipment]]) {
self.shipmentIdsToUpdate = shipmentIdsToUpdate
self.shipments = shipments
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import Foundation
import Codegen

/// Response after updating shipments of an order
///
public struct WooShippingUpdateShipmentResponse: Decodable, Equatable, GeneratedFakeable, GeneratedCopiable {
public let siteID: Int64
public let orderID: Int64
public let shipments: [String: [WooShippingShipment]]

public init(siteID: Int64,
orderID: Int64,
shipments: [String: [WooShippingShipment]]) {
self.siteID = siteID
self.orderID = orderID
self.shipments = shipments
}

private enum CodingKeys: String, CodingKey {
case data
}

public init(from decoder: Decoder) throws {
guard let siteID = decoder.userInfo[.siteID] as? Int64 else {
throw WooShippingUpdateShipmentDecodingError.missingSiteID
}

guard let orderID = decoder.userInfo[.orderID] as? Int64 else {
throw WooShippingUpdateShipmentDecodingError.missingOrderID
}

let container = try decoder.container(keyedBy: CodingKeys.self)
let shipments: [String: [WooShippingShipment]] = {
guard let shipmentsString = try? container.decodeIfPresent(String.self, forKey: .data),
let data = shipmentsString.data(using: .utf8) else {
return [:]
}

return (try? JSONDecoder().decode([String: [WooShippingShipment]].self, from: data)) ?? [:]
}()

self.init(siteID: siteID,
orderID: orderID,
shipments: shipments)
}
}

// MARK: - Decoding Errors
//
enum WooShippingUpdateShipmentDecodingError: Error {
case missingSiteID
case missingOrderID
}
33 changes: 33 additions & 0 deletions Networking/Networking/Remote/WooShippingRemote.swift
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ public protocol WooShippingRemoteProtocol {
func loadConfig(siteID: Int64,
orderID: Int64,
completion: @escaping (Result<WooShippingConfig, Error>) -> Void)
func updateShipment(siteID: Int64,
orderID: Int64,
shipmentToUpdate: WooShippingUpdateShipment,
completion: @escaping (Result<WooShippingUpdateShipmentResponse, Error>) -> Void)
}

/// Shipping Labels Remote Endpoints for the WooShipping Plugin.
Expand Down Expand Up @@ -430,6 +434,32 @@ public final class WooShippingRemote: Remote, WooShippingRemoteProtocol {
enqueue(request, mapper: mapper, completion: completion)
}
}

/// Updates shipment for a given order
/// - Parameters:
/// - siteID: Remote ID of the site.
/// - orderID: Remote ID of the order.
/// - shipmentToUpdate: shipment info to send to remote
/// - completion: Closure to be executed upon completion.
public func updateShipment(siteID: Int64,
orderID: Int64,
shipmentToUpdate: WooShippingUpdateShipment,
completion: @escaping (Result<WooShippingUpdateShipmentResponse, Error>) -> Void) {
do {
let parameters = try shipmentToUpdate.toDictionary()
let path = Path.updateShipment(orderID: orderID)
let request = JetpackRequest(wooApiVersion: .wooShipping,
method: .post,
siteID: siteID,
path: path,
parameters: parameters,
availableAsRESTRequest: true)
let mapper = WooShippingUpdateShipmentMapper(siteID: siteID, orderID: orderID)
enqueue(request, mapper: mapper, completion: completion)
} catch {
completion(.failure(error))
}
}
}

// MARK: Constants
Expand All @@ -453,6 +483,9 @@ private extension WooShippingRemote {
static func config(orderID: Int64) -> String {
"config/label-purchase/\(orderID)"
}
static func updateShipment(orderID: Int64) -> String {
"shipments/\(orderID)"
}
}

enum ParameterKey {
Expand Down
Loading