Skip to content

Commit 1968981

Browse files
Merge pull request #642 from algolia/feat/ensure-bool-string-deserialization
Ensure bool-representable enums deserialization from string
2 parents 362676b + 9c47ca3 commit 1968981

File tree

16 files changed

+131
-33
lines changed

16 files changed

+131
-33
lines changed
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
//
2+
// BoolContainer.swift
3+
//
4+
//
5+
// Created by Vladislav Fitc on 17/07/2020.
6+
//
7+
8+
import Foundation
9+
10+
/**
11+
Helper structure ensuring the decoding of a bool value from a "volatile" JSON
12+
occasionally providing bool values in the form of String
13+
*/
14+
struct BoolContainer: RawRepresentable, Codable {
15+
16+
let rawValue: Bool
17+
18+
init(rawValue: Bool) {
19+
self.rawValue = rawValue
20+
}
21+
22+
init(from decoder: Decoder) throws {
23+
let container = try decoder.singleValueContainer()
24+
if let boolValue = try? container.decode(Bool.self) {
25+
self.rawValue = boolValue
26+
return
27+
}
28+
if let boolFromString = Bool(try container.decode(String.self)) {
29+
self.rawValue = boolFromString
30+
return
31+
}
32+
throw DecodingError.dataCorruptedError(in: container, debugDescription: "Value cannot be decoded neither to Bool nor to String representing Bool value")
33+
}
34+
35+
}

Sources/AlgoliaSearchClient/Helpers/Coding/Wrappers/StringNumberContainer.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77

88
import Foundation
99

10+
/**
11+
Helper structure ensuring the decoding of a number value from a "volatile" JSON
12+
occasionally providing number values in the form of String
13+
*/
1014
struct StringNumberContainer: RawRepresentable, Codable {
1115

1216
let rawValue: Double

Sources/AlgoliaSearchClient/Models/Rule/Rule+Alternatives.swift

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import Foundation
99

1010
extension Rule {
1111

12-
public enum Alternatives: RawRepresentable, Codable {
12+
public enum Alternatives: RawRepresentable, Encodable {
1313

1414
case `true`
1515
case `false`
@@ -30,3 +30,20 @@ extension Rule {
3030
}
3131

3232
}
33+
34+
extension Rule.Alternatives: ExpressibleByBooleanLiteral {
35+
36+
public init(booleanLiteral value: Bool) {
37+
self = value ? .true : .false
38+
}
39+
40+
}
41+
42+
extension Rule.Alternatives: Decodable {
43+
44+
public init(from decoder: Decoder) throws {
45+
let boolContainer = try BoolContainer(from: decoder)
46+
self = boolContainer.rawValue ? .true : .false
47+
}
48+
49+
}

Sources/AlgoliaSearchClient/Models/Search/Indexing/PartialUpdate.swift

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -148,15 +148,13 @@ extension PartialUpdate.Content: Codable {
148148
}
149149

150150
public init(from decoder: Decoder) throws {
151-
let container = try decoder.container(keyedBy: CodingKeys.self)
152-
let operation: PartialUpdate.Operation? = try container.decodeIfPresent(forKey: .operation)
153-
if let operation = operation {
154-
self.operation = operation
155-
self.value = try container.decode(forKey: .value)
156-
} else {
151+
guard let container = try? decoder.container(keyedBy: CodingKeys.self), container.contains(.operation) else {
157152
self.operation = nil
158153
self.value = try JSON(from: decoder)
154+
return
159155
}
156+
self.operation = try container.decode(forKey: .operation)
157+
self.value = try container.decode(forKey: .value)
160158
}
161159

162160
public func encode(to encoder: Encoder) throws {

Sources/AlgoliaSearchClient/Models/Settings/Auxiliary/LanguageFeature.swift

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -39,17 +39,7 @@ extension LanguageFeature: ExpressibleByArrayLiteral {
3939

4040
}
4141

42-
extension LanguageFeature: Codable {
43-
44-
public init(from decoder: Decoder) throws {
45-
let container = try decoder.singleValueContainer()
46-
if let isTrue = try? container.decode(Bool.self) {
47-
self = isTrue ? .true : .false
48-
} else {
49-
let languages = try container.decode([Language].self)
50-
self = .queryLanguages(languages)
51-
}
52-
}
42+
extension LanguageFeature: Encodable {
5343

5444
public func encode(to encoder: Encoder) throws {
5545
var singleValueContainer = encoder.singleValueContainer()
@@ -65,6 +55,20 @@ extension LanguageFeature: Codable {
6555

6656
}
6757

58+
extension LanguageFeature: Decodable {
59+
60+
public init(from decoder: Decoder) throws {
61+
let container = try decoder.singleValueContainer()
62+
if let boolContainer = try? container.decode(BoolContainer.self) {
63+
self = boolContainer.rawValue ? .true : .false
64+
} else {
65+
let languages = try container.decode([Language].self)
66+
self = .queryLanguages(languages)
67+
}
68+
}
69+
70+
}
71+
6872
extension LanguageFeature: Equatable {}
6973

7074
extension LanguageFeature: URLEncodable {

Tests/AlgoliaSearchClientTests/Doc/APIParameters/APIParameters+Typos.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ extension APIParameters {
7676
func set_default_typo_tolerance_mode() {
7777
let settings = Settings()
7878
.set(\.typoTolerance, to: true)
79-
//.set(\.typoTolerance, to: .false)
79+
//.set(\.typoTolerance, to: false)
8080
//.set(\.typoTolerance, to: .min)
8181
//.set(\.typoTolerance, to: .strict)
8282

@@ -89,8 +89,8 @@ extension APIParameters {
8989

9090
func override_default_typo_tolerance_mode() {
9191
let query = Query("query")
92-
.set(\.typoTolerance, to: .false)
93-
//.set(\.typoTolerance, to: .true)
92+
.set(\.typoTolerance, to: false)
93+
//.set(\.typoTolerance, to: true)
9494
//.set(\.typoTolerance, to: .min)
9595
//.set(\.typoTolerance, to: .strict)
9696

Tests/AlgoliaSearchClientTests/Doc/Methods/ABTestSnippets.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ extension ABTestSnippets {
7979
variantB: .init(indexName: "indexName2",
8080
trafficPercentage: 10,
8181
customSearchParameters: Query()
82-
.set(\.ignorePlurals, to: .true),
82+
.set(\.ignorePlurals, to: true),
8383
description: "a description"))
8484

8585
analyticsClient.addABTest(abTest) { result in

Tests/AlgoliaSearchClientTests/Doc/Methods/RulesSnippets.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ extension RulesSnippets {
5252
.set(\.condition, to: Rule.Condition()
5353
.set(\.anchoring, to: .contains)
5454
.set(\.pattern, to: .literal("smartphone"))
55-
.set(\.alternatives, to: .true)
55+
.set(\.alternatives, to: true)
5656
)
5757
.set(\.consequence, to: Rule.Consequence()
5858
.set(\.query, to: Query()

Tests/AlgoliaSearchClientTests/Helper/XCTest+Codable.swift

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,33 @@ func AssertEncodeDecode<T: Codable>(_ value: T, _ rawValue: JSON, file: StaticSt
1414
try AssertDecode(rawValue, expected: value, file: file, line: line)
1515
}
1616

17+
func AssertDecode<T: Codable & Equatable>(_ input: JSON, expected: T, file: StaticString = #file, line: UInt = #line) throws {
18+
19+
let encoder = JSONEncoder()
20+
encoder.dateEncodingStrategy = .swiftAPIClient
21+
let data = try encoder.encode(input)
22+
23+
let jsonDecoder = JSONDecoder()
24+
jsonDecoder.dateDecodingStrategy = .swiftAPIClient
25+
let decoded = try jsonDecoder.decode(T.self, from: data)
26+
27+
XCTAssertEqual(expected, decoded, file: file, line: line)
28+
}
29+
1730
func AssertDecode<T: Codable>(_ input: JSON, expected: T, file: StaticString = #file, line: UInt = #line) throws {
1831

1932
let encoder = JSONEncoder()
2033
encoder.dateEncodingStrategy = .swiftAPIClient
21-
let data = try encoder.encode(expected)
34+
let data = try encoder.encode(input)
2235

2336
let jsonDecoder = JSONDecoder()
2437
jsonDecoder.dateDecodingStrategy = .swiftAPIClient
25-
let expectedJSON = try jsonDecoder.decode(JSON.self, from: data)
38+
let decoded = try jsonDecoder.decode(T.self, from: data)
39+
40+
let decodedJSON = try JSON(decoded)
41+
let expectedJSON = try JSON(expected)
2642

27-
XCTAssertEqual(input, expectedJSON, file: file, line: line)
43+
XCTAssertEqual(expectedJSON, decodedJSON, file: file, line: line)
2844
}
2945

3046
@discardableResult func AssertDecode<T: Decodable>(jsonFilename filename: String, expected: T.Type, file: StaticString = #file, line: UInt = #line) throws -> T {

Tests/AlgoliaSearchClientTests/Integration/QueryRulesIntegrationTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ class QueryRulesIntegrationTests: OnlineTestCase {
4848
try index.saveRule(brandAutomaticFacetingRule).wait()
4949

5050
let queryEditsRule = Rule(objectID: "query_edits")
51-
.set(\.condition, to: .init(anchoring: .is, pattern: .literal("mobile phone"), alternatives: .true))
51+
.set(\.condition, to: .init(anchoring: .is, pattern: .literal("mobile phone"), alternatives: true))
5252
.set(\.consequence, to: Rule.Consequence()
5353
.set(\.filterPromotes, to: false)
5454
.set(\.queryTextAlteration, to: .edits([

0 commit comments

Comments
 (0)