Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
11 changes: 10 additions & 1 deletion Modules/Sources/Networking/Model/SiteAPI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,16 @@ public struct SiteAPI: Decodable, Equatable, GeneratedFakeable {
}

let siteAPIContainer = try decoder.container(keyedBy: SiteAPIKeys.self)
let namespaces = siteAPIContainer.failsafeDecodeIfPresent([String].self, forKey: .namespaces) ?? []

/// Some third-party plugins (like CoCart API) alter the response of `namespaces` field into a dictionary instead of array.
/// This workaround transforms the unexpected dictionary to extract the values in the dictionary.
let namespaces = siteAPIContainer.failsafeDecodeIfPresent(
targetType: [String].self,
forKey: .namespaces, alternativeTypes: [
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Super nit: I'd put the alternativeTypes: [ on the next line to keep the indentation consistent

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in c7889b0 👍

.dictionary(transform: { Array($0.values) })
]
) ?? []

let authentication = try? siteAPIContainer.decode(Authentication.self, forKey: .authentication)
let applicationPasswordAvailable = authentication?.applicationPasswords?.endpoints?.authorization != nil

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ public enum AlternativeDecodingType<T> {
case string(transform: (String) -> T)
case bool(transform: (Bool) -> T)
case integer(transform: (Int) -> T)
case dictionary(transform: ([String: String]) -> T)
}

// MARK: - KeyedDecodingContainer: Bulletproof JSON Decoding.
Expand Down Expand Up @@ -60,6 +61,10 @@ public extension KeyedDecodingContainer {
if let result = failsafeDecodeIfPresent(integerForKey: key) {
return transform(result)
}
case .dictionary(let transform):
if let result = failsafeDecodeIfPresent([String: String].self, forKey: key) {
return transform(result)
}
}
}
return nil
Expand Down
20 changes: 19 additions & 1 deletion Modules/Tests/NetworkingTests/Mapper/SiteAPIMapperTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import XCTest

/// SiteAPIMapperTests Unit Tests
///
class SiteAPIMapperTests: XCTestCase {
final class SiteAPIMapperTests: XCTestCase {

/// Dummy Site ID.
///
Expand Down Expand Up @@ -54,6 +54,18 @@ class SiteAPIMapperTests: XCTestCase {
XCTAssertEqual(apiSettings?.namespaces, dummyBrokenNamespaces)
XCTAssertEqual(apiSettings?.highestWooVersion, WooAPIVersion.none)
}

/// Verifies the SiteSetting fields are parsed correctly.
///
func test_SiteSetting_with_malformed_namespaces_fields_are_properly_parsed() {
let apiSettings = mapLoadSiteAPIResponseWithMalformedNamespaces()

XCTAssertNotNil(apiSettings)
XCTAssertEqual(apiSettings?.siteID, dummySiteID)
XCTAssertNotNil(apiSettings?.namespaces)
XCTAssertEqual(apiSettings?.namespaces.sorted(), dummyNamespaces.sorted())
XCTAssertEqual(apiSettings?.highestWooVersion, WooAPIVersion.mark3)
}
}


Expand Down Expand Up @@ -88,4 +100,10 @@ private extension SiteAPIMapperTests {
func mapLoadBrokenSiteAPIResponse() -> SiteAPI? {
return mapSiteAPIData(from: "site-api-no-woo")
}

/// Returns the SiteAPIMapper output with malformed namespaces
///
func mapLoadSiteAPIResponseWithMalformedNamespaces() -> SiteAPI? {
return mapSiteAPIData(from: "site-api-malformed")
}
}
23 changes: 23 additions & 0 deletions Modules/Tests/NetworkingTests/Responses/site-api-malformed.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"data": {
"namespaces": {
"0": "oembed\/1.0",
"1": "akismet\/v1",
"2": "jetpack\/v4",
"3": "wpcom\/v2",
"4": "wc\/v1",
"5": "wc\/v2",
"6": "wc\/v3",
"7": "wc-pb\/v3",
"8": "wp\/v2"
},
"authentication": [],
"_links": {
"help": [
{
"href": "http:\/\/v2.wp-api.org\/"
}
]
}
}
}
3 changes: 3 additions & 0 deletions WooCommerce/Classes/Yosemite/DefaultStoresManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -166,8 +166,10 @@ class DefaultStoresManager: StoresManager {

if case .wpcom = credentials {
listenToWPCOMInvalidWPCOMTokenNotification()
applicationPasswordGenerationFailureObserver = nil
} else {
listenToApplicationPasswordGenerationFailureNotification()
invalidWPCOMTokenNotificationObserver = nil
}

return self
Expand Down Expand Up @@ -250,6 +252,7 @@ class DefaultStoresManager: StoresManager {
@discardableResult
func deauthenticate() -> StoresManager {
applicationPasswordGenerationFailureObserver = nil
invalidWPCOMTokenNotificationObserver = nil

if isAuthenticated {
let resetAction = CardPresentPaymentAction.reset
Expand Down