Skip to content

Commit a85c7a7

Browse files
authored
Fixes and tests for decoding binary items and empty lists in DynamoDBEvent.Decoder (#103)
Cleaned up version of [#61](#61) that also has tests. This PR makes it so that in lists, the decoder will not try to access the 0th index of an empty array. This PR will also make it so that `AttributeValue`s of type `.binary` will properly decode to base64 Strings when String is put as the type in the the `Decodable`. To avoid including `Data` in the code, I included code from the original source of the base 64 decoding methods (https://github.com/fabianfett/swift-base64-kit) to add in the corresponding encoding methods. However, I saw lots of Foundation is imported throughout so I reverted it. What are your thoughts on this matter? I also added tests that aim to make these failure points more resilient in the future.
1 parent f6fc221 commit a85c7a7

File tree

2 files changed

+70
-6
lines changed

2 files changed

+70
-6
lines changed

Sources/AWSLambdaEvents/DynamoDB.swift

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -462,11 +462,14 @@ extension DynamoDBEvent {
462462
func decode(_ type: String.Type, forKey key: K) throws -> String {
463463
let value = try getValue(forKey: key)
464464

465-
guard case .string(let string) = value else {
465+
switch value {
466+
case .string(let string):
467+
return string
468+
case .binary(let binary):
469+
return Data(binary).base64EncodedString()
470+
default:
466471
throw self.createTypeMismatchError(type: type, forKey: key, value: value)
467472
}
468-
469-
return string
470473
}
471474

472475
func decode(_ type: Double.Type, forKey key: K) throws -> Double {
@@ -651,11 +654,14 @@ extension DynamoDBEvent {
651654
}
652655

653656
func decode(_: String.Type) throws -> String {
654-
guard case .string(let string) = self.value else {
657+
switch self.value {
658+
case .string(let string):
659+
return string
660+
case .binary(let binary):
661+
return Data(binary).base64EncodedString()
662+
default:
655663
throw self.createTypeMismatchError(type: String.self, value: self.value)
656664
}
657-
658-
return string
659665
}
660666

661667
func decode(_: Double.Type) throws -> Double {
@@ -769,6 +775,9 @@ extension DynamoDBEvent {
769775
self.codingPath = codingPath
770776
self.array = array
771777
self.count = array.count
778+
779+
// No need to decode if array is empty
780+
self.isAtEnd = array.isEmpty
772781
}
773782

774783
mutating func decodeNil() throws -> Bool {

Tests/AWSLambdaEventsTests/DynamoDBTests.swift

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,4 +251,59 @@ struct DynamoDBTests {
251251
#expect(test?.foo == "bar")
252252
#expect(test?.xyz == 123)
253253
}
254+
255+
@Test func decoderEmptyList() {
256+
let value: [String: DynamoDBEvent.AttributeValue] = [
257+
"fooList": .list([])
258+
]
259+
260+
struct Test: Codable {
261+
let fooList: [Int]
262+
}
263+
264+
let test = try? DynamoDBEvent.Decoder().decode(Test.self, from: value)
265+
#expect(test?.fooList == [])
266+
}
267+
268+
@Test func decoderNonEmptyList() {
269+
let value: [String: DynamoDBEvent.AttributeValue] = [
270+
"fooList": .list([.string("test")])
271+
]
272+
273+
struct Test: Codable {
274+
let fooList: [String]
275+
}
276+
277+
let test = try? DynamoDBEvent.Decoder().decode(Test.self, from: value)
278+
#expect(test?.fooList == ["test"])
279+
}
280+
281+
@Test func decoderBinaryToBase64KeyedDecodingContainer() {
282+
let value: [String: DynamoDBEvent.AttributeValue] = [
283+
"xyz": .binary([0x74, 0x65, 0x73, 0x74]) // UTF8 for "test"
284+
]
285+
286+
struct Test: Codable {
287+
let xyz: String
288+
}
289+
290+
let test = try? DynamoDBEvent.Decoder().decode(Test.self, from: value)
291+
#expect(test?.xyz == "dGVzdA==") // base64 for "test"
292+
}
293+
294+
@Test func decoderBinaryToBase64SingleValueDecodingContainer() {
295+
let value: DynamoDBEvent.AttributeValue = .binary([0x74, 0x65, 0x73, 0x74]) // UTF8 for "test"
296+
297+
let test = try? DynamoDBEvent.Decoder().decode(String.self, from: value)
298+
#expect(test == "dGVzdA==") // base64 for "test"
299+
}
300+
301+
@Test func decoderBinaryToBase64UnkeyedDecodingContainer() {
302+
let value: DynamoDBEvent.AttributeValue = .list([
303+
.binary([0x74, 0x65, 0x73, 0x74]) // UTF8 for "test"
304+
])
305+
306+
let test = try? DynamoDBEvent.Decoder().decode([String].self, from: value)
307+
#expect(test == ["dGVzdA=="]) // base64 for "test"
308+
}
254309
}

0 commit comments

Comments
 (0)