Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Modules/Sources/Fakes/NetworkingCore.generated.swift
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ extension NetworkingCore.DotcomError {
/// Returns a "ready to use" type filled with fake values.
///
public static func fake() -> NetworkingCore.DotcomError {
.empty
.empty()
}
}
extension NetworkingCore.MetaContainer {
Expand Down
2 changes: 1 addition & 1 deletion Modules/Sources/Networking/Remote/AccountRemote.swift
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ public enum CreateAccountError: Error, Equatable {
/// Decodable Initializer.
///
init(dotcomError error: DotcomError) {
if case let .unknown(code, message) = error {
if case let .unknown(code, message, _) = error {
switch code {
case Constants.emailExists:
self = .emailExists
Expand Down
2 changes: 1 addition & 1 deletion Modules/Sources/Networking/Remote/DevicesRemote.swift
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public class DevicesRemote: Remote {

enqueue(request, mapper: mapper) { (success, error) in
guard success == true else {
completion(error ?? DotcomError.empty)
completion(error ?? DotcomError.empty())
return
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ public enum InstallThemeError: Error {

init?(_ error: Error) {
guard let dotcomError = error as? DotcomError,
case let .unknown(code, _) = dotcomError else {
case let .unknown(code, _, _) = dotcomError else {
return nil
}

Expand Down
52 changes: 28 additions & 24 deletions Modules/Sources/NetworkingCore/Model/DotcomError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,79 +7,79 @@ public enum DotcomError: Error, Decodable, Equatable, GeneratedFakeable {

/// Non explicit reason
///
case empty
case empty(data: [String: AnyDecodable]? = nil)

/// Missing Token!
///
case unauthorized
case unauthorized(data: [String: AnyDecodable]? = nil)

/// We're not properly authenticated
///
case invalidToken
case invalidToken(data: [String: AnyDecodable]? = nil)

/// Remote Request Failed
///
case requestFailed
case requestFailed(data: [String: AnyDecodable]? = nil)

/// No route was found matching the URL and request method
///
case noRestRoute
case noRestRoute(data: [String: AnyDecodable]? = nil)

/// Jetpack is not connected
///
/// This can be caused by an `unknown_token` error from Jetpack
/// or an `invalid_blog` error from WordPress.com Stats.
///
case jetpackNotConnected
case jetpackNotConnected(data: [String: AnyDecodable]? = nil)

/// Unknown: Represents an unmapped remote error. Capisce?
///
case unknown(code: String, message: String?)
case unknown(code: String, message: String?, data: [String: AnyDecodable]?)

/// Stats error cases - API documentation of possible errors:
/// https://developer.wordpress.com/docs/api/1.1/get/sites/%24site/stats/
/// Note: when the cases get large, consider refactoring them into a separate error enum that conforms to a Dotcom error protocol

/// No permission to view site stats
case noStatsPermission
case noStatsPermission(data: [String: AnyDecodable]? = nil)

/// Jetpack site stats module disabled
case statsModuleDisabled
case statsModuleDisabled(data: [String: AnyDecodable]? = nil)

/// The requested resourced does not exist remotely
case resourceDoesNotExist
case resourceDoesNotExist(data: [String: AnyDecodable]? = nil)

/// Decodable Initializer.
///
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let error = try container.decode(String.self, forKey: .error)
let message = try container.decodeIfPresent(String.self, forKey: .message)
let data = try container.decodeIfPresent([String: AnyDecodable].self, forKey: .data)

switch error {
case Constants.invalidToken:
self = .invalidToken
self = .invalidToken(data: data)
case Constants.requestFailed:
self = .requestFailed
self = .requestFailed(data: data)
case Constants.unauthorized where message == ErrorMessages.noStatsPermission:
self = .noStatsPermission
self = .noStatsPermission(data: data)
case Constants.unauthorized:
self = .unauthorized
self = .unauthorized(data: data)
case Constants.noRestRoute:
self = .noRestRoute
self = .noRestRoute(data: data)
case Constants.invalidBlog where message == ErrorMessages.statsModuleDisabled:
self = .statsModuleDisabled
self = .statsModuleDisabled(data: data)
case Constants.restTermInvalid where message == ErrorMessages.resourceDoesNotExist:
self = .resourceDoesNotExist
self = .resourceDoesNotExist(data: data)
case Constants.unknownToken,
Constants.invalidBlog where message == ErrorMessages.jetpackNotConnected:
self = .jetpackNotConnected
self = .jetpackNotConnected(data: data)
default:
self = .unknown(code: error, message: message)
self = .unknown(code: error, message: message, data: data)
}
}


/// Constants for Possible Error Identifiers
///
private enum Constants {
Expand All @@ -97,6 +97,7 @@ public enum DotcomError: Error, Decodable, Equatable, GeneratedFakeable {
private enum CodingKeys: String, CodingKey {
case error
case message
case data
}

/// Possible Error Messages
Expand Down Expand Up @@ -134,7 +135,7 @@ extension DotcomError: CustomStringConvertible {
return NSLocalizedString("Dotcom Resource does not exist", comment: "WordPress.com error thrown when a requested resource does not exist remotely.")
case .jetpackNotConnected:
return NSLocalizedString("Jetpack Not Connected", comment: "WordPress.com error thrown when Jetpack is not connected.")
case .unknown(let code, let message):
case .unknown(let code, let message, _):
let theMessage = message ?? String()
let messageFormat = NSLocalizedString(
"Dotcom Error: [%1$@] %2$@",
Expand All @@ -150,14 +151,17 @@ extension DotcomError: CustomStringConvertible {
//
public func ==(lhs: DotcomError, rhs: DotcomError) -> Bool {
switch (lhs, rhs) {
case (.requestFailed, .requestFailed),
case (.empty, .empty),
(.unauthorized, .unauthorized),
(.invalidToken, .invalidToken),
(.requestFailed, .requestFailed),
(.noRestRoute, .noRestRoute),
(.jetpackNotConnected, .jetpackNotConnected),
(.noStatsPermission, .noStatsPermission),
(.statsModuleDisabled, .statsModuleDisabled),
(.jetpackNotConnected, .jetpackNotConnected):
(.resourceDoesNotExist, .resourceDoesNotExist):
return true
case let (.unknown(codeLHS, _), .unknown(codeRHS, _)):
case let (.unknown(codeLHS, _, _), .unknown(codeRHS, _, _)):
return codeLHS == codeRHS
default:
return false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ public final class NotificationsRemote: Remote, NotificationsRemoteProtocol {

enqueue(request, mapper: mapper) { (success, error) in
guard success == true else {
completion(error ?? DotcomError.empty)
completion(error ?? DotcomError.empty())
return
}

Expand All @@ -99,7 +99,7 @@ public final class NotificationsRemote: Remote, NotificationsRemoteProtocol {

enqueue(request, mapper: mapper) { (success, error) in
guard success == true else {
completion(error ?? DotcomError.empty)
completion(error ?? DotcomError.empty())
return
}

Expand Down
2 changes: 1 addition & 1 deletion Modules/Sources/Yosemite/Stores/CouponsError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ public struct CouponsError: Error, LocalizedError {

public init?(underlyingError error: Error) {
switch error {
case DotcomError.unknown(Constants.invalidCouponCode, let message):
case DotcomError.unknown(Constants.invalidCouponCode, let message, _):
self.message = message ?? Localizations.defaultCouponsError
self.underlyingError = error
case let NetworkError.unacceptableStatusCode(_, response):
Expand Down
2 changes: 1 addition & 1 deletion Modules/Sources/Yosemite/Stores/OrderStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -663,7 +663,7 @@ private extension OrderStore {
})
case .failure(let error):
if let dotcomError = error as? DotcomError,
case let .unknown(code, message) = dotcomError {
case let .unknown(code, message, _) = dotcomError {
switch code {
case "woocommerce_rest_gift_card_cannot_apply":
return onCompletion(.failure(GiftCardError.cannotApply(reason: message)))
Expand Down
2 changes: 1 addition & 1 deletion Modules/Sources/Yosemite/Stores/PaymentsError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public enum PaymentsError: Error, LocalizedError {

private static func unwrapError(error: Error) -> PaymentsErrorConvertible? {
switch error {
case let DotcomError.unknown(code, message):
case let DotcomError.unknown(code, message, _):
return PaymentsDotcomErrorDetails(code: code, message: message)
case let NetworkError.unacceptableStatusCode(_, response):
guard let response,
Expand Down
11 changes: 6 additions & 5 deletions Modules/Sources/Yosemite/Stores/ProductStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -317,8 +317,9 @@ private extension ProductStore {
shouldDeleteExistingProducts: shouldDeleteExistingProducts)
let hasNextPage = products.count == pageSize
return hasNextPage
} catch let error as DotcomError where error == .unknown(code: "rest_invalid_param", message: "Invalid parameter(s): type") {
if let productType,
} catch let error as DotcomError {
if case .unknown(code: "rest_invalid_param", message: "Invalid parameter(s): type", data: _) = error,
let productType,
ProductType.coreTypes.contains(productType) == false {
return false
}
Expand Down Expand Up @@ -684,7 +685,7 @@ private extension ProductStore {
feature: .productDetailsFromScannedTexts,
responseFormat: .json)
guard let jsonData = jsonString.data(using: .utf8) else {
return completion(.failure(DotcomError.resourceDoesNotExist))
return completion(.failure(DotcomError.resourceDoesNotExist()))
}
let details = try JSONDecoder().decode(ProductDetailsFromScannedTexts.self, from: jsonData)
completion(.success(.init(name: details.name, description: details.description)))
Expand Down Expand Up @@ -1388,7 +1389,7 @@ public enum ProductUpdateError: Error, Equatable {
return
}
switch dotcomError {
case let .unknown(code, message):
case let .unknown(code, message, _):
guard let errorCode = ErrorCode(rawValue: code) else {
self = .unknown(error: dotcomError.toAnyError)
return
Expand Down Expand Up @@ -1476,7 +1477,7 @@ public enum ProductLoadError: Error, Equatable {
case unknown(error: AnyError)

init(underlyingError error: Error) {
guard case let DotcomError.unknown(code, _) = error else {
guard case let DotcomError.unknown(code, _, _) = error else {
self = .unknown(error: error.toAnyError)
return
}
Expand Down
4 changes: 2 additions & 2 deletions Modules/Sources/Yosemite/Stores/ShippingLabelStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ private extension ShippingLabelStore {
}
onCompletion(eligibility.isEligible)
case .failure(let error):
if error as? DotcomError == .noRestRoute {
if case .noRestRoute = error as? DotcomError {
DDLogError("⚠️ Endpoint for shipping label creation eligibility is unreachable for order: \(orderID). WC Shipping plugin may be missing.")
} else {
DDLogError("⛔️ Error checking shipping label creation eligibility for order \(orderID): \(error)")
Expand Down Expand Up @@ -492,7 +492,7 @@ public enum PackageCreationError: Error, Equatable {
return
}
switch dotcomError {
case .unknown(let code, _):
case .unknown(let code, _, _):
guard let errorCode = ErrorCode(rawValue: code) else {
self = .unknown(error: dotcomError.toAnyError)
return
Expand Down
2 changes: 1 addition & 1 deletion Modules/Sources/Yosemite/Stores/SiteStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ public enum SiteCreationError: Error, Equatable {
}
case let remoteError as DotcomError:
switch remoteError {
case let .unknown(code, _):
case let .unknown(code, _, _):
switch code {
case "blog_name_exists":
self = .domainExists
Expand Down
2 changes: 1 addition & 1 deletion Modules/Sources/Yosemite/Stores/WooShippingStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ private extension WooShippingStore {
}
onCompletion(eligibility.isEligible)
case .failure(let error):
if error as? DotcomError == .noRestRoute {
if case .noRestRoute = error as? DotcomError {
DDLogError("⚠️ Endpoint for shipping label creation eligibility is unreachable for order: \(orderID). WC Shipping plugin may be missing.")
} else {
DDLogError("⛔️ Error checking shipping label creation eligibility for order \(orderID): \(error)")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,9 @@ final public class CommonReaderConfigProvider: CommonReaderConfigProviding {
private extension CardReaderConfigError {
init?(error: Error) {
switch error {
case DotcomError.unknown(code: "store_address_is_incomplete", let message):
case DotcomError.unknown(code: "store_address_is_incomplete", let message, _):
self = .incompleteStoreAddress(adminUrl: URL(string: message ?? ""))
case DotcomError.unknown(code: "postal_code_invalid", _):
case DotcomError.unknown(code: "postal_code_invalid", _, _):
self = .invalidPostalCode
case NetworkError.unacceptableStatusCode(_, let responseData):
guard let responseData,
Expand Down
47 changes: 23 additions & 24 deletions Modules/Sources/Yosemite/Tools/POS/POSOrderService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -143,35 +143,34 @@ private extension POSOrderService {
_ error: Error,
cart: POSCart
) -> [CartOrderComparison.MissingCartItem]? {
// Check if this is an AFError wrapping a DotcomError or NetworkError
let underlyingError: Error? = {
if let afError = error as? AFError {
return afError.underlyingError
let (code, data) = errorCodeAndData(from: error)
guard let code, isProductValidationError(code: code) else {
return nil
}

guard let variationID = extractVariationID(from: data) else {
return unknownMissingProductsInCart()
}
return createMissingProductInfo(forVariationID: variationID, cart: cart)
}

func errorCodeAndData(from error: Error) -> (code: String?, data: [String: AnyDecodable]?) {
// Unwrap AFError if present
let underlyingError: Error = {
if let afError = error as? AFError, let underlying = afError.underlyingError {
return underlying
}
return error
}()

// Check for DotcomError with product/variation validation error codes
if case .unknown(let code, _) = underlyingError as? DotcomError {
if isProductValidationError(code: code) {
// DotcomError doesn't include data field, fall back to generic error
return extractMissingProductsFromCart()
}
// Extract error code and data from either error type
if case .unknown(let code, _, let data) = underlyingError as? DotcomError {
return (code, data)
}

// Check for NetworkError with product/variation validation error codes
if let networkError = underlyingError as? NetworkError,
let errorCode = networkError.errorCode,
isProductValidationError(code: errorCode) {
// Try to extract variation_id from NetworkError data if available
if let variationID = extractVariationID(from: networkError.errorData) {
return createMissingProductInfo(forVariationID: variationID, cart: cart)
}
// Fall back to generic error if no variation_id in data
return extractMissingProductsFromCart()
if let networkError = underlyingError as? NetworkError {
return (networkError.errorCode, networkError.errorData)
}

return nil
return (nil, nil)
}

/// Extracts variation_id from error data dictionary
Expand Down Expand Up @@ -237,7 +236,7 @@ private extension POSOrderService {

/// Extracts missing products by trying to identify items in cart that might have caused the validation error
/// Since server doesn't tell us which specific products failed, we return generic error info
func extractMissingProductsFromCart() -> [CartOrderComparison.MissingCartItem]? {
func unknownMissingProductsInCart() -> [CartOrderComparison.MissingCartItem] {
// We can't determine which specific products are invalid from the server error
// So we return a generic missing product message with 0 for both IDs (meaning unknown)
// The user will need to remove all products and retry
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ class CommentRemoteTests: XCTestCase {
return
}

XCTAssert(error == .unauthorized)
XCTAssert(error == .unauthorized())
XCTAssertNil(updatedStatus)

expectation.fulfill()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ final class JetpackSettingsRemoteTests: XCTestCase {
// When
try await remote.enableJetpackModule(for: sampleSiteID, moduleSlug: "invalidmodule")
}, errorAssert: { error in
(error as? DotcomError) == .unknown(code: "some_updated", message: "Invalid option: invalidmodule.")
(error as? DotcomError) == .unknown(code: "some_updated", message: "Invalid option: invalidmodule.", data: nil)
})
}

Expand Down
Loading