Skip to content

Commit

Permalink
Best effort convert OF lists
Browse files Browse the repository at this point in the history
  • Loading branch information
fabriziodemaria committed Apr 8, 2024
1 parent 3120a42 commit a7ddf66
Show file tree
Hide file tree
Showing 3 changed files with 114 additions and 6 deletions.
43 changes: 38 additions & 5 deletions Sources/Confidence/ConfidenceValue.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,6 @@ public class ConfidenceValue: Equatable, Encodable {
self.value = .timestamp(timestamp)
}

// TODO: Handle heterogeneous types
public init(valueList: [ConfidenceValue]) {
self.value = .list(valueList.map { $0.value })
}

public init(boolList: [Bool]) {
self.value = .list(boolList.map { .boolean($0) })
}
Expand All @@ -57,6 +52,9 @@ public class ConfidenceValue: Equatable, Encodable {
self.value = .list(doubleList.map { .double($0) })
}

public init(nullList: [()]) {
self.value = .list(nullList.map { .null })
}

public init(dateList: [DateComponents]) {
self.value = .list(dateList.map { .date($0) })
Expand Down Expand Up @@ -150,6 +148,29 @@ public class ConfidenceValue: Equatable, Encodable {
return false
}

public func type() -> ConfidenceValueType {
switch value {
case .boolean(_):

Check warning on line 153 in Sources/Confidence/ConfidenceValue.swift

View workflow job for this annotation

GitHub Actions / SwiftLint

Empty Enum Arguments Violation: Arguments can be omitted when matching enums with associated values if they are not used (empty_enum_arguments)
return .boolean
case .string(_):

Check warning on line 155 in Sources/Confidence/ConfidenceValue.swift

View workflow job for this annotation

GitHub Actions / SwiftLint

Empty Enum Arguments Violation: Arguments can be omitted when matching enums with associated values if they are not used (empty_enum_arguments)
return .string
case .integer(_):

Check warning on line 157 in Sources/Confidence/ConfidenceValue.swift

View workflow job for this annotation

GitHub Actions / SwiftLint

Empty Enum Arguments Violation: Arguments can be omitted when matching enums with associated values if they are not used (empty_enum_arguments)
return .integer
case .double(_):

Check warning on line 159 in Sources/Confidence/ConfidenceValue.swift

View workflow job for this annotation

GitHub Actions / SwiftLint

Empty Enum Arguments Violation: Arguments can be omitted when matching enums with associated values if they are not used (empty_enum_arguments)
return .double
case .date(_):

Check warning on line 161 in Sources/Confidence/ConfidenceValue.swift

View workflow job for this annotation

GitHub Actions / SwiftLint

Empty Enum Arguments Violation: Arguments can be omitted when matching enums with associated values if they are not used (empty_enum_arguments)
return .date
case .timestamp(_):

Check warning on line 163 in Sources/Confidence/ConfidenceValue.swift

View workflow job for this annotation

GitHub Actions / SwiftLint

Empty Enum Arguments Violation: Arguments can be omitted when matching enums with associated values if they are not used (empty_enum_arguments)
return .timestamp
case .list(_):

Check warning on line 165 in Sources/Confidence/ConfidenceValue.swift

View workflow job for this annotation

GitHub Actions / SwiftLint

Empty Enum Arguments Violation: Arguments can be omitted when matching enums with associated values if they are not used (empty_enum_arguments)
return .list
case .structure(_):

Check warning on line 167 in Sources/Confidence/ConfidenceValue.swift

View workflow job for this annotation

GitHub Actions / SwiftLint

Empty Enum Arguments Violation: Arguments can be omitted when matching enums with associated values if they are not used (empty_enum_arguments)
return .structure
case .null:
return .null
}
}

public static func == (lhs: ConfidenceValue, rhs: ConfidenceValue) -> Bool {
lhs.value == rhs.value
}
Expand All @@ -162,6 +183,18 @@ extension ConfidenceValue {
}
}

public enum ConfidenceValueType: CaseIterable {
case boolean
case string
case integer
case double
case date
case timestamp
case list
case structure
case null
}


/// Serializable data structure meant for event sending via Confidence
private enum ConfidenceValueInternal: Equatable, Encodable {
Expand Down
32 changes: 31 additions & 1 deletion Sources/ConfidenceProvider/Utils/ConfidenceTypeMapper.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,37 @@ public enum ConfidenceTypeMapper {
case .date(let value):
return ConfidenceValue(timestamp: value)
case .list(let values):
return ConfidenceValue(valueList: values.compactMap(convertValue))
let types = Set(values.map(convertValue).map { $0.type() } )

Check warning on line 29 in Sources/ConfidenceProvider/Utils/ConfidenceTypeMapper.swift

View workflow job for this annotation

GitHub Actions / SwiftLint

Closing Brace Spacing Violation: Closing brace with closing parenthesis should not have any whitespaces in the middle (closing_brace)
guard types.count == 1, let listType = types.first else {
return ConfidenceValue.init(nullList: [()])
}
switch listType {
case .boolean:
return ConfidenceValue.init(boolList: values.compactMap { $0.asBoolean() })
case .string:
return ConfidenceValue.init(stringList: values.compactMap { $0.asString() })
case .integer:
return ConfidenceValue.init(integerList: values.compactMap { $0.asInteger() })
case .double:
return ConfidenceValue.init(doubleList: values.compactMap { $0.asDouble() })
// Currently Date Value is converted to Timestamp ConfidenceValue to not lose precision, so this should never happen
case .date:
let componentsToExtract: Set<Calendar.Component> = [.year, .month, .day]
return ConfidenceValue.init(dateList: values.compactMap {
guard let date = $0.asDate() else {
return nil
}
return Calendar.current.dateComponents(componentsToExtract, from: date ) })

Check warning on line 49 in Sources/ConfidenceProvider/Utils/ConfidenceTypeMapper.swift

View workflow job for this annotation

GitHub Actions / SwiftLint

Closure End Indentation Violation: Closure end should have the same indentation as the line that started it; expected 16, got 93 (closure_end_indentation)
case .timestamp:
return ConfidenceValue.init(timestampList: values.compactMap { $0.asDate() })
case .list:
return ConfidenceValue.init(nullList: values.compactMap { _ in () }) // List of list not allowed
case .structure:
return ConfidenceValue.init(nullList: values.compactMap { _ in () }) // TODO: List of structures
case .null:
return ConfidenceValue.init(nullList: values.compactMap { _ in () })

}
case .structure(let values):
return ConfidenceValue(structure: values.compactMapValues(convertValue))
case .null:
Expand Down
45 changes: 45 additions & 0 deletions Tests/ConfidenceProviderTests/ConfidenceTypeMapperTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,51 @@ class ValueConverterTest: XCTestCase {
XCTAssertEqual(confidenceStruct, expected)
}

func testContextConversionWithLists() throws {
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
let date1 = try XCTUnwrap(formatter.date(from: "2022-01-01 12:00:00"))
let date2 = try XCTUnwrap(formatter.date(from: "2022-01-02 12:00:00"))

let openFeatureCtx = MutableContext(
targetingKey: "userid",
structure: MutableStructure(attributes: ([
"stringList": .list([.string("test1"), .string("test2")]),
"boolList": .list([.boolean(true), .boolean(false)]),
"integerList": .list([.integer(11), .integer(33)]),
"doubleList": .list([.double(3.14), .double(1.0)]),
"dateList": .list([.date(date1), .date(date2)]),
"nullList": .list([.null, .null]),
"listList": .list([.list([.string("nested_value1")]), .list([.string("nested_value2")])]),
"structList": .list([.structure(["test": .string("nested_test1")]), .structure(["test": .string("nested_test2")])])
])))
let confidenceStruct = ConfidenceTypeMapper.from(ctx: openFeatureCtx)
let expected = [
"stringList": ConfidenceValue(stringList: ["test1", "test2"]),
"boolList": ConfidenceValue(boolList: [true, false]),
"integerList": ConfidenceValue(integerList: [11, 33]),
"doubleList": ConfidenceValue(doubleList: [3.14, 1.0]),
"dateList": ConfidenceValue(timestampList: [date1, date2]),
"nullList": ConfidenceValue(nullList: [(),()]),
"listList": ConfidenceValue(nullList: [(),()]),
"structList": ConfidenceValue(nullList: [(),()]),
"targeting_key": ConfidenceValue(string: "userid")
]
XCTAssertEqual(confidenceStruct, expected)
}

func testContextConversionWithHeterogenousLists() throws {
let openFeatureCtx = MutableContext(
targetingKey: "userid",
structure: MutableStructure(attributes: (["key": .list([.string("test1"), .integer(1)])])))
let confidenceStruct = ConfidenceTypeMapper.from(ctx: openFeatureCtx)
let expected = [
"key": ConfidenceValue(nullList: [()]),
"targeting_key": ConfidenceValue(string: "userid")
]
XCTAssertEqual(confidenceStruct, expected)
}

func testValueConversion() throws {
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
Expand Down

0 comments on commit a7ddf66

Please sign in to comment.