Skip to content

Commit 81b2451

Browse files
authored
Functions Serializer Updates (#13409)
1 parent f5b09e3 commit 81b2451

File tree

2 files changed

+68
-74
lines changed

2 files changed

+68
-74
lines changed

FirebaseFunctions/Sources/Internal/FunctionsSerializer.swift

Lines changed: 23 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -20,21 +20,21 @@ private enum Constants {
2020
static let dateType = "type.googleapis.com/google.protobuf.Timestamp"
2121
}
2222

23-
enum SerializerError: Error {
24-
// TODO: Add parameters class name and value
25-
case unsupportedType // (className: String, value: AnyObject)
26-
case unknownNumberType(charValue: String, number: NSNumber)
27-
case invalidValueForType(value: String, requestedType: String)
23+
extension FUNSerializer {
24+
enum Error: Swift.Error {
25+
case unsupportedType(typeName: String)
26+
case unknownNumberType(charValue: String, number: NSNumber)
27+
case invalidValueForType(value: String, requestedType: String)
28+
}
2829
}
2930

3031
class FUNSerializer: NSObject {
31-
private let dateFormatter: DateFormatter
32-
33-
override init() {
34-
dateFormatter = DateFormatter()
35-
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"
36-
dateFormatter.timeZone = TimeZone(identifier: "UTC")
37-
}
32+
private let dateFormatter: DateFormatter = {
33+
let formatter = DateFormatter()
34+
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"
35+
formatter.timeZone = TimeZone(identifier: "UTC")
36+
return formatter
37+
}()
3838

3939
// MARK: - Internal APIs
4040

@@ -67,7 +67,7 @@ class FUNSerializer: NSObject {
6767
return encoded
6868

6969
} else {
70-
throw SerializerError.unsupportedType
70+
throw Error.unsupportedType(typeName: typeName(of: object))
7171
}
7272
}
7373

@@ -86,21 +86,8 @@ class FUNSerializer: NSObject {
8686
}
8787

8888
let decoded = NSMutableDictionary()
89-
var decodeError: Error?
90-
dict.enumerateKeysAndObjects { key, obj, stopPointer in
91-
do {
92-
let decodedItem = try self.decode(obj)
93-
decoded[key] = decodedItem
94-
} catch {
95-
decodeError = error
96-
stopPointer.pointee = true
97-
return
98-
}
99-
}
100-
101-
// Throw the internal error that popped up, if it did.
102-
if let decodeError {
103-
throw decodeError
89+
try dict.forEach { key, value in
90+
decoded[key] = try decode(value)
10491
}
10592
return decoded
10693
} else if let array = object as? NSArray {
@@ -116,11 +103,15 @@ class FUNSerializer: NSObject {
116103
return object as AnyObject
117104
}
118105

119-
throw SerializerError.unsupportedType
106+
throw Error.unsupportedType(typeName: typeName(of: object))
120107
}
121108

122109
// MARK: - Private Helpers
123110

111+
private func typeName(of value: Any) -> String {
112+
String(describing: type(of: value))
113+
}
114+
124115
private func encodeNumber(_ number: NSNumber) throws -> AnyObject {
125116
// Recover the underlying type of the number, using the method described here:
126117
// http://stackoverflow.com/questions/2518761/get-type-of-nsnumber
@@ -163,7 +154,7 @@ class FUNSerializer: NSObject {
163154

164155
default:
165156
// All documented codes should be handled above, so this shouldn"t happen.
166-
throw SerializerError.unknownNumberType(charValue: String(cType[0]), number: number)
157+
throw Error.unknownNumberType(charValue: String(cType[0]), number: number)
167158
}
168159
}
169160

@@ -172,7 +163,7 @@ class FUNSerializer: NSObject {
172163
case Constants.longType:
173164
let formatter = NumberFormatter()
174165
guard let n = formatter.number(from: value) else {
175-
throw SerializerError.invalidValueForType(value: value, requestedType: type)
166+
throw Error.invalidValueForType(value: value, requestedType: type)
176167
}
177168
return n
178169

@@ -182,7 +173,7 @@ class FUNSerializer: NSObject {
182173
var endPtr: UnsafeMutablePointer<CChar>?
183174
let returnValue = UInt64(strtoul(str, &endPtr, 10))
184175
guard String(returnValue) == value else {
185-
throw SerializerError.invalidValueForType(value: value, requestedType: type)
176+
throw Error.invalidValueForType(value: value, requestedType: type)
186177
}
187178
return NSNumber(value: returnValue)
188179

FirebaseFunctions/Tests/Unit/SerializerTests.swift

Lines changed: 45 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -25,67 +25,64 @@ import FirebaseCore
2525
import XCTest
2626

2727
class SerializerTests: XCTestCase {
28+
private var serializer: FUNSerializer!
29+
30+
override func setUp() {
31+
super.setUp()
32+
serializer = FUNSerializer()
33+
}
34+
2835
func testEncodeNull() throws {
29-
let serializer = FUNSerializer()
3036
let null = NSNull()
3137
XCTAssertEqual(try serializer.encode(null) as? NSNull, null)
3238
}
3339

3440
func testDecodeNull() throws {
35-
let serializer = FUNSerializer()
3641
let null = NSNull()
3742
XCTAssertEqual(try serializer.decode(null) as? NSNull, null)
3843
}
3944

4045
func testEncodeInt32() throws {
41-
let serializer = FUNSerializer()
4246
let one = NSNumber(value: 1 as Int32)
4347
XCTAssertEqual(one, try serializer.encode(one) as? NSNumber)
4448
}
4549

4650
func testEncodeInt() throws {
47-
let serializer = FUNSerializer()
4851
let one = NSNumber(1)
4952
let dict = try XCTUnwrap(serializer.encode(one) as? NSDictionary)
5053
XCTAssertEqual("type.googleapis.com/google.protobuf.Int64Value", dict["@type"] as? String)
5154
XCTAssertEqual("1", dict["value"] as? String)
5255
}
5356

5457
func testDecodeInt32() throws {
55-
let serializer = FUNSerializer()
5658
let one = NSNumber(value: 1 as Int32)
5759
XCTAssertEqual(one, try serializer.decode(one) as? NSNumber)
5860
}
5961

6062
func testDecodeInt() throws {
61-
let serializer = FUNSerializer()
6263
let one = NSNumber(1)
6364
XCTAssertEqual(one, try serializer.decode(one) as? NSNumber)
6465
}
6566

6667
func testDecodeIntFromDictionary() throws {
67-
let serializer = FUNSerializer()
6868
let dictOne = ["@type": "type.googleapis.com/google.protobuf.Int64Value",
6969
"value": "1"]
7070
XCTAssertEqual(NSNumber(1), try serializer.decode(dictOne) as? NSNumber)
7171
}
7272

7373
func testEncodeLong() throws {
74-
let serializer = FUNSerializer()
7574
let lowLong = NSNumber(-9_223_372_036_854_775_800)
7675
let dict = try XCTUnwrap(serializer.encode(lowLong) as? NSDictionary)
7776
XCTAssertEqual("type.googleapis.com/google.protobuf.Int64Value", dict["@type"] as? String)
7877
XCTAssertEqual("-9223372036854775800", dict["value"] as? String)
7978
}
8079

8180
func testDecodeLong() throws {
82-
let serializer = FUNSerializer()
8381
let lowLong = NSNumber(-9_223_372_036_854_775_800)
8482
XCTAssertEqual(lowLong, try serializer.decode(lowLong) as? NSNumber)
8583
}
8684

8785
func testDecodeLongFromDictionary() throws {
88-
let serializer = FUNSerializer()
8986
let dictLowLong = ["@type": "type.googleapis.com/google.protobuf.Int64Value",
9087
"value": "-9223372036854775800"]
9188
let decoded = try serializer.decode(dictLowLong) as? NSNumber
@@ -96,13 +93,12 @@ class SerializerTests: XCTestCase {
9693
}
9794

9895
func testDecodeInvalidLong() throws {
99-
let serializer = FUNSerializer()
10096
let typeString = "type.googleapis.com/google.protobuf.Int64Value"
10197
let badVal = "-9223372036854775800 and some other junk"
10298
let dictLowLong = ["@type": typeString, "value": badVal]
10399
do {
104100
_ = try serializer.decode(dictLowLong) as? NSNumber
105-
} catch let SerializerError.invalidValueForType(value, type) {
101+
} catch let FUNSerializer.Error.invalidValueForType(value, type) {
106102
XCTAssertEqual(value, badVal)
107103
XCTAssertEqual(type, typeString)
108104
return
@@ -111,7 +107,6 @@ class SerializerTests: XCTestCase {
111107
}
112108

113109
func testEncodeUnsignedLong() throws {
114-
let serializer = FUNSerializer()
115110
let typeString = "type.googleapis.com/google.protobuf.UInt64Value"
116111
let highULong = NSNumber(value: 18_446_744_073_709_551_607 as UInt64)
117112
let expected = ["@type": typeString, "value": "18446744073709551607"]
@@ -120,13 +115,11 @@ class SerializerTests: XCTestCase {
120115
}
121116

122117
func testDecodeUnsignedLong() throws {
123-
let serializer = FUNSerializer()
124118
let highULong = NSNumber(value: 18_446_744_073_709_551_607 as UInt64)
125119
XCTAssertEqual(highULong, try serializer.decode(highULong) as? NSNumber)
126120
}
127121

128122
func testDecodeUnsignedLongFromDictionary() throws {
129-
let serializer = FUNSerializer()
130123
let typeString = "type.googleapis.com/google.protobuf.UInt64Value"
131124
let highULong = NSNumber(value: 18_446_744_073_709_551_607 as UInt64)
132125
let coded = ["@type": typeString, "value": "18446744073709551607"]
@@ -138,13 +131,12 @@ class SerializerTests: XCTestCase {
138131
}
139132

140133
func testDecodeUnsignedLongFromDictionaryOverflow() throws {
141-
let serializer = FUNSerializer()
142134
let typeString = "type.googleapis.com/google.protobuf.UInt64Value"
143135
let tooHighVal = "18446744073709551616"
144136
let coded = ["@type": typeString, "value": tooHighVal]
145137
do {
146138
_ = try serializer.decode(coded) as? NSNumber
147-
} catch let SerializerError.invalidValueForType(value, type) {
139+
} catch let FUNSerializer.Error.invalidValueForType(value, type) {
148140
XCTAssertEqual(value, tooHighVal)
149141
XCTAssertEqual(type, typeString)
150142
return
@@ -153,47 +145,39 @@ class SerializerTests: XCTestCase {
153145
}
154146

155147
func testEncodeDouble() throws {
156-
let serializer = FUNSerializer()
157148
let myDouble = NSNumber(value: 1.2 as Double)
158149
XCTAssertEqual(myDouble, try serializer.encode(myDouble) as? NSNumber)
159150
}
160151

161152
func testDecodeDouble() throws {
162-
let serializer = FUNSerializer()
163153
let myDouble = NSNumber(value: 1.2 as Double)
164154
XCTAssertEqual(myDouble, try serializer.decode(myDouble) as? NSNumber)
165155
}
166156

167157
func testEncodeBool() throws {
168-
let serializer = FUNSerializer()
169158
XCTAssertEqual(true, try serializer.encode(true) as? NSNumber)
170159
}
171160

172161
func testDecodeBool() throws {
173-
let serializer = FUNSerializer()
174162
XCTAssertEqual(true, try serializer.decode(true) as? NSNumber)
175163
}
176164

177165
func testEncodeString() throws {
178-
let serializer = FUNSerializer()
179166
XCTAssertEqual("hello", try serializer.encode("hello") as? String)
180167
}
181168

182169
func testDecodeString() throws {
183-
let serializer = FUNSerializer()
184170
XCTAssertEqual("good-bye", try serializer.decode("good-bye") as? String)
185171
}
186172

187173
// TODO: Should we add support for Array as well as NSArray?
188174

189175
func testEncodeSimpleArray() throws {
190-
let serializer = FUNSerializer()
191176
let input = [1 as Int32, 2 as Int32] as NSArray
192177
XCTAssertEqual(input, try serializer.encode(input) as? NSArray)
193178
}
194179

195180
func testEncodeArray() throws {
196-
let serializer = FUNSerializer()
197181
let input = [
198182
1 as Int32,
199183
"two",
@@ -204,7 +188,6 @@ class SerializerTests: XCTestCase {
204188
}
205189

206190
func testDecodeArray() throws {
207-
let serializer = FUNSerializer()
208191
let input = [
209192
1 as Int64,
210193
"two",
@@ -227,44 +210,64 @@ class SerializerTests: XCTestCase {
227210
"baz": [3, ["@type": "type.googleapis.com/google.protobuf.Int64Value",
228211
"value": "9876543210"]] as [Any],
229212
] as NSDictionary
230-
let serializer = FUNSerializer()
231213
XCTAssertEqual(expected, try serializer.encode(input) as? NSDictionary)
232214
}
233215

234216
func testDecodeMap() {
235217
let input = ["foo": 1, "bar": "hello", "baz": [3, 9_876_543_210]] as NSDictionary
236218
let expected = ["foo": 1, "bar": "hello", "baz": [3, 9_876_543_210]] as NSDictionary
237-
let serializer = FUNSerializer()
238219
XCTAssertEqual(expected, try serializer.decode(input) as? NSDictionary)
239220
}
240221

241222
func testEncodeUnknownType() {
242223
let input = ["@type": "unknown", "value": "whatever"] as NSDictionary
243-
let serializer = FUNSerializer()
244224
XCTAssertEqual(input, try serializer.encode(input) as? NSDictionary)
245225
}
246226

247227
func testDecodeUnknownType() {
248228
let input = ["@type": "unknown", "value": "whatever"] as NSDictionary
249-
let serializer = FUNSerializer()
250229
XCTAssertEqual(input, try serializer.decode(input) as? NSDictionary)
251230
}
252231

253232
func testDecodeUnknownTypeWithoutValue() {
254233
let input = ["@type": "unknown"] as NSDictionary
255-
let serializer = FUNSerializer()
256234
XCTAssertEqual(input, try serializer.decode(input) as? NSDictionary)
257235
}
258236

259-
// - (void)testDecodeUnknownTypeWithoutValue {
260-
// NSDictionary *input = @{
261-
// @"@type" : @"unknown",
262-
// };
263-
// FUNSerializer *serializer = [[FUNSerializer alloc] init];
264-
// NSError *error = nil;
265-
// XCTAssertEqualObjects(input, [serializer decode:input error:&error]);
266-
// XCTAssertNil(error);
267-
// }
268-
//
269-
// @end
237+
func testEncodeUnsupportedType() {
238+
let input = CustomObject()
239+
240+
do {
241+
let _ = try serializer.encode(input)
242+
XCTFail("Expected an error")
243+
} catch {
244+
guard case let .unsupportedType(typeName: typeName) = error as? FUNSerializer.Error
245+
else {
246+
return XCTFail("Unexpected error: \(error)")
247+
}
248+
249+
XCTAssertEqual(typeName, "CustomObject")
250+
}
251+
}
252+
253+
func testDecodeUnsupportedType() {
254+
let input = CustomObject()
255+
256+
do {
257+
let _ = try serializer.decode(input)
258+
XCTFail("Expected an error")
259+
} catch {
260+
guard case let .unsupportedType(typeName: typeName) = error as? FUNSerializer.Error
261+
else {
262+
return XCTFail("Unexpected error: \(error)")
263+
}
264+
265+
XCTAssertEqual(typeName, "CustomObject")
266+
}
267+
}
268+
}
269+
270+
/// Used to represent a type that cannot be encoded or decoded.
271+
private struct CustomObject {
272+
let id = 123
270273
}

0 commit comments

Comments
 (0)