Skip to content

Commit fe22deb

Browse files
Fixed: unsafeOverwrite will now properly consider CodableContextKeys (#6)
1 parent 94f5bef commit fe22deb

File tree

2 files changed

+36
-11
lines changed

2 files changed

+36
-11
lines changed

Sources/ApodiniContext/Context.swift

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,12 @@ import OrderedCollections
1111

1212
private class ContextBox {
1313
var entries: [ObjectIdentifier: StoredContextValue]
14+
/// Mapping from ``CodableContextKey/identifier`` to base64 encoded data
15+
var decodedEntries: [String: String]
1416

15-
init(_ entries: [ObjectIdentifier: StoredContextValue]) {
17+
init(_ entries: [ObjectIdentifier: StoredContextValue], _ decodedEntries: [String: String]) {
1618
self.entries = entries
19+
self.decodedEntries = decodedEntries
1720
}
1821
}
1922

@@ -26,15 +29,17 @@ struct StoredContextValue {
2629
/// A `Context` holds a collection of values for predefined `ContextKey`s or `OptionalContextKey`s.
2730
public struct Context: ContextKeyRetrievable {
2831
private var boxedEntries: ContextBox
32+
2933
private var entries: [ObjectIdentifier: StoredContextValue] {
3034
boxedEntries.entries
3135
}
32-
/// Mapping from ``CodableContextKey/identifier`` to base64 encoded data
33-
private let decodedEntries: [String: String]
36+
37+
private var decodedEntries: [String: String] {
38+
boxedEntries.decodedEntries
39+
}
3440

3541
init(_ entries: [ObjectIdentifier: StoredContextValue] = [:], _ decodedEntries: [String: String] = [:]) {
36-
self.boxedEntries = ContextBox(entries)
37-
self.decodedEntries = decodedEntries
42+
self.boxedEntries = ContextBox(entries, decodedEntries)
3843
}
3944

4045
/// Create a new empty ``Context``.
@@ -97,14 +102,21 @@ public struct Context: ContextKeyRetrievable {
97102
precondition(entries[key] == nil || allowOverwrite, "Cannot overwrite existing ContextKey entry with `unsafeAdd`: \(C.self): \(value)")
98103
if let codableContextKey = contextKey as? AnyCodableContextKey.Type {
99104
// we need to prevent this. as Otherwise we would need to handle merging this stuff which get really complex
100-
precondition(decodedEntries[codableContextKey.identifier] == nil, "Cannot overwrite existing CodableContextKey entry with `unsafeAdd`: \(C.self): \(value)")
105+
precondition(
106+
decodedEntries[codableContextKey.identifier] == nil || allowOverwrite,
107+
"Cannot overwrite existing CodableContextKey entry with `unsafeAdd`: \(C.self): \(value)"
108+
)
109+
110+
// if we reach this point, either the key doesn't exist or `allowOverwrite` was turned on
111+
// and we need to remove the existing entry in order to properly overwrite everything
112+
boxedEntries.decodedEntries.removeValue(forKey: codableContextKey.identifier)
101113
}
102114

103115
boxedEntries.entries[key] = StoredContextValue(key: contextKey, value: value)
104116
}
105117

106118
private func checkForDecodedEntries<Key: CodableContextKey>(for key: Key.Type = Key.self) -> Key.Value? {
107-
guard let dataValue = decodedEntries[Key.identifier] else {
119+
guard let dataValue = boxedEntries.decodedEntries.removeValue(forKey: Key.identifier) else {
108120
return nil
109121
}
110122

@@ -140,15 +152,13 @@ extension Context: Codable {
140152
public init(from decoder: Decoder) throws {
141153
let container = try decoder.container(keyedBy: StringContextKey.self)
142154

143-
self.boxedEntries = ContextBox([:])
144-
145155
var decodedEntries: [String: String] = [:]
146156

147157
for key in container.allKeys {
148158
decodedEntries[key.stringValue] = try container.decode(String.self, forKey: key)
149159
}
150160

151-
self.decodedEntries = decodedEntries
161+
self.boxedEntries = ContextBox([:], decodedEntries)
152162
}
153163

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

Tests/ApodiniContextTests/ContextKeyTests.swift

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ class ContextKeyTests: XCTestCase {
118118
XCTAssertEqual(decodedContext.get(valueFor: CodableArrayStringContextKey.self), ["Hello Sun"])
119119
}
120120

121-
func testUnsafeAddAllowingOverwrite() {
121+
func testUnsafeAddAllowingOverwrite() throws {
122122
struct CodableStringContextKey: CodableContextKey {
123123
typealias Value = String
124124
}
@@ -129,5 +129,20 @@ class ContextKeyTests: XCTestCase {
129129
XCTAssertEqual(context.get(valueFor: CodableStringContextKey.self), "Hello World")
130130
context.unsafeAdd(CodableStringContextKey.self, value: "Hello Mars", allowOverwrite: true)
131131
XCTAssertEqual(context.get(valueFor: CodableStringContextKey.self), "Hello Mars")
132+
133+
let encoder = FineJSONEncoder()
134+
encoder.jsonSerializeOptions = .init(isPrettyPrint: false)
135+
let decoder = FineJSONDecoder()
136+
137+
let encodedContext = try encoder.encode(context)
138+
XCTAssertEqual(
139+
String(data: encodedContext, encoding: .utf8),
140+
"{\"CodableStringContextKey\":\"IkhlbGxvIE1hcnMi\"}"
141+
)
142+
143+
let decodedContext = try decoder.decode(Context.self, from: encodedContext)
144+
145+
decodedContext.unsafeAdd(CodableStringContextKey.self, value: "Hello Saturn", allowOverwrite: true)
146+
XCTAssertEqual(decodedContext.get(valueFor: CodableStringContextKey.self), "Hello Saturn")
132147
}
133148
}

0 commit comments

Comments
 (0)