Skip to content

Commit d27cf92

Browse files
Support for AnyValue Scalar type
1 parent 8e6b8a2 commit d27cf92

File tree

7 files changed

+506
-0
lines changed

7 files changed

+506
-0
lines changed

Sources/Scalars/AnyValue.swift

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// Copyright 2024 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
import Foundation
16+
17+
/**
18+
AnyValue represents the Any graphql scalar, which represents a scalar Codable data (Int, Double, String) or a JSON object
19+
20+
*/
21+
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
22+
public struct AnyValue {
23+
public private(set) var value: Data
24+
25+
public init(codableValue: Codable) throws {
26+
do {
27+
let jsonEncoder = JSONEncoder()
28+
value = try jsonEncoder.encode(codableValue)
29+
}
30+
}
31+
32+
public func decodeValue<T: Decodable>(_ type: T.Type) throws -> T? {
33+
do {
34+
let jsonDecoder = JSONDecoder()
35+
let decodedResult = try jsonDecoder.decode(type, from: value)
36+
return decodedResult
37+
}
38+
}
39+
}
40+
41+
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
42+
extension AnyValue: Codable {
43+
public init(from decoder: any Decoder) throws {
44+
let singleValueContainer = try decoder.singleValueContainer()
45+
value = try singleValueContainer.decode(Data.self)
46+
47+
print("decoded data \(value.count)")
48+
print("decoded data string \(String(data: value, encoding: .utf8))")
49+
}
50+
51+
public func encode(to encoder: any Encoder) throws {
52+
var container = encoder.singleValueContainer()
53+
try container.encode(value)
54+
}
55+
}
56+
57+
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
58+
extension AnyValue: Equatable {
59+
public static func == (lhs: Self, rhs: Self) -> Bool {
60+
return lhs.value == rhs.value
61+
}
62+
}
63+
64+
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
65+
extension AnyValue: Hashable {
66+
public func hash(into hasher: inout Hasher) {
67+
hasher.combine(value)
68+
}
69+
}
Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
// Copyright 2024 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
import XCTest
16+
17+
import FirebaseCore
18+
@testable import FirebaseDataConnect
19+
20+
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
21+
final class AnyScalarTests: IntegrationTestBase {
22+
override func setUp(completion: @escaping ((any Error)?) -> Void) {
23+
Task {
24+
do {
25+
try await ProjectConfigurator.shared.configureProject()
26+
completion(nil)
27+
} catch {
28+
completion(error)
29+
}
30+
}
31+
}
32+
33+
func testAnyValueString() async throws {
34+
let testData = "Test String Data \(Int.random(in: 1 ... 10000))"
35+
let anyTestData = try AnyValue(codableValue: testData)
36+
37+
let anyValueId = UUID()
38+
_ = try await DataConnect.kitchenSinkClient.createAnyValueTypeMutationRef(
39+
id: anyValueId,
40+
props: anyTestData
41+
).execute()
42+
print(anyValueId)
43+
44+
let result = try await DataConnect.kitchenSinkClient.getAnyValueTypeQueryRef(id: anyValueId)
45+
.execute()
46+
let anyValueResult = result.data.anyValueType?.props
47+
let decodedResult = try anyValueResult?.decodeValue(String.self)
48+
print(decodedResult)
49+
50+
XCTAssertEqual(testData, decodedResult)
51+
}
52+
53+
func testAnyValueInt() async throws {
54+
let testNumber = Int.random(in: 1 ... 9999)
55+
print("testNumber \(testNumber)")
56+
let anyTestData = try AnyValue(codableValue: testNumber)
57+
58+
let anyValueId = UUID()
59+
_ = try await DataConnect.kitchenSinkClient.createAnyValueTypeMutationRef(
60+
id: anyValueId,
61+
props: anyTestData
62+
).execute()
63+
64+
let result = try await DataConnect.kitchenSinkClient.getAnyValueTypeQueryRef(id: anyValueId)
65+
.execute()
66+
let anyValueResult = result.data.anyValueType?.props
67+
let decodedResult = try anyValueResult?.decodeValue(Int.self)
68+
print("decodedNumber \(decodedResult)")
69+
XCTAssertEqual(testNumber, decodedResult)
70+
}
71+
72+
func testAnyValueDouble() async throws {
73+
let testDouble = Double
74+
.random(in: Double.leastNormalMagnitude ... Double.greatestFiniteMagnitude)
75+
let anyTestData = try AnyValue(codableValue: testDouble)
76+
77+
let anyValueId = UUID()
78+
_ = try await DataConnect.kitchenSinkClient.createAnyValueTypeMutationRef(
79+
id: anyValueId,
80+
props: anyTestData
81+
).execute()
82+
83+
let result = try await DataConnect.kitchenSinkClient.getAnyValueTypeQueryRef(id: anyValueId)
84+
.execute()
85+
let anyValueResult = result.data.anyValueType?.props
86+
let decodedResult = try anyValueResult?.decodeValue(Double.self)
87+
88+
XCTAssertEqual(testDouble, decodedResult)
89+
}
90+
91+
func testAnyValueInt64() async throws {
92+
let testInt64 = Int64.random(in: Int64.min ... Int64.max)
93+
let anyTestData = try AnyValue(codableValue: testInt64)
94+
95+
let anyValueId = UUID()
96+
_ = try await DataConnect.kitchenSinkClient.createAnyValueTypeMutationRef(
97+
id: anyValueId,
98+
props: anyTestData
99+
).execute()
100+
101+
let result = try await DataConnect.kitchenSinkClient.getAnyValueTypeQueryRef(id: anyValueId)
102+
.execute()
103+
let anyValueResult = result.data.anyValueType?.props
104+
let decodedResult = try anyValueResult?.decodeValue(Int64.self)
105+
106+
XCTAssertEqual(testInt64, decodedResult)
107+
}
108+
109+
func testAnyValueInt64Max() async throws {
110+
let int64Max = Int64.max
111+
let anyTestData = try AnyValue(codableValue: int64Max)
112+
113+
let anyValueId = UUID()
114+
_ = try await DataConnect.kitchenSinkClient
115+
.createAnyValueTypeMutationRef(id: anyValueId, props: anyTestData).execute()
116+
117+
let result = try await DataConnect.kitchenSinkClient.getAnyValueTypeQueryRef(id: anyValueId)
118+
.execute()
119+
let anyValueResult = result.data.anyValueType?.props
120+
let decodedResult = try anyValueResult?.decodeValue(Int64.self)
121+
122+
XCTAssertEqual(int64Max, decodedResult)
123+
}
124+
125+
func testAnyValueInt64Min() async throws {
126+
let int64Min = Int64.min
127+
let anyTestData = try AnyValue(codableValue: int64Min)
128+
129+
let anyValueId = UUID()
130+
_ = try await DataConnect.kitchenSinkClient
131+
.createAnyValueTypeMutationRef(id: anyValueId, props: anyTestData).execute()
132+
133+
let result = try await DataConnect.kitchenSinkClient.getAnyValueTypeQueryRef(id: anyValueId)
134+
.execute()
135+
let anyValueResult = result.data.anyValueType?.props
136+
let decodedResult = try anyValueResult?.decodeValue(Int64.self)
137+
138+
XCTAssertEqual(int64Min, decodedResult)
139+
}
140+
141+
func testAnyValueDoubleMax() async throws {
142+
let testDouble = Double.greatestFiniteMagnitude
143+
let anyTestData = try AnyValue(codableValue: testDouble)
144+
145+
let anyValueId = UUID()
146+
_ = try await DataConnect.kitchenSinkClient.createAnyValueTypeMutationRef(
147+
id: anyValueId,
148+
props: anyTestData
149+
).execute()
150+
151+
let result = try await DataConnect.kitchenSinkClient.getAnyValueTypeQueryRef(id: anyValueId)
152+
.execute()
153+
let anyValueResult = result.data.anyValueType?.props
154+
let decodedResult = try anyValueResult?.decodeValue(Double.self)
155+
156+
XCTAssertEqual(testDouble, decodedResult)
157+
}
158+
159+
func testAnyValueDoubleMin() async throws {
160+
let testDouble = Double.leastNormalMagnitude
161+
let anyTestData = try AnyValue(codableValue: testDouble)
162+
163+
let anyValueId = UUID()
164+
_ = try await DataConnect.kitchenSinkClient.createAnyValueTypeMutationRef(
165+
id: anyValueId,
166+
props: anyTestData
167+
).execute()
168+
169+
let result = try await DataConnect.kitchenSinkClient.getAnyValueTypeQueryRef(id: anyValueId)
170+
.execute()
171+
let anyValueResult = result.data.anyValueType?.props
172+
let decodedResult = try anyValueResult?.decodeValue(Double.self)
173+
174+
XCTAssertEqual(testDouble, decodedResult)
175+
}
176+
177+
struct AnyValueEmbeddedStruct: Codable, Equatable {
178+
var stringVal = "Hello World \(Int.random(in: 1 ... 1000))"
179+
var doubleVal = Double.random(in: 1 ... 10000)
180+
var dictValInt: [String: Int] = [
181+
"keyOne": Int.random(in: 1 ... 1000),
182+
"KeyTwo": Int.random(in: 1 ... 100_000),
183+
]
184+
185+
static func == (lhs: AnyValueEmbeddedStruct, rhs: AnyValueEmbeddedStruct) -> Bool {
186+
return lhs.stringVal == rhs.stringVal &&
187+
lhs.doubleVal == rhs.doubleVal &&
188+
lhs.dictValInt == rhs.dictValInt
189+
}
190+
}
191+
192+
struct AnyValueTestStruct: Codable, Equatable {
193+
var stringVal = "Test Data \(Int.random(in: 10 ... 1000))"
194+
var intVal = Int.random(in: 1 ... 10000)
195+
var doubleVal = Double.random(in: 1 ... 1000)
196+
var dictVal: [String: String] = ["key1": "val1", "key2": "val2"]
197+
var dictValDouble: [String: Double] = [
198+
"key1": Double.random(in: 1 ... 1000),
199+
"key2": Double.random(in: 1 ... 10000),
200+
]
201+
var structVal = AnyValueEmbeddedStruct()
202+
203+
static func == (lhs: AnyValueTestStruct, rhs: AnyValueTestStruct) -> Bool {
204+
return lhs.stringVal == rhs.stringVal &&
205+
lhs.intVal == rhs.intVal &&
206+
lhs.doubleVal == rhs.doubleVal &&
207+
lhs.dictVal == rhs.dictVal &&
208+
lhs.dictValDouble == rhs.dictValDouble
209+
}
210+
}
211+
212+
func testAnyValueStruct() async throws {
213+
let structVal = AnyValueTestStruct()
214+
let anyValStruct = try AnyValue(codableValue: structVal)
215+
216+
let anyValueId = UUID()
217+
_ = try await DataConnect.kitchenSinkClient.createAnyValueTypeMutationRef(
218+
id: anyValueId,
219+
props: anyValStruct
220+
).execute()
221+
222+
let result = try await DataConnect.kitchenSinkClient.getAnyValueTypeQueryRef(id: anyValueId)
223+
.execute()
224+
let anyValueResult = result.data.anyValueType?.props
225+
let decodedResult = try anyValueResult?.decodeValue(AnyValueTestStruct.self)
226+
227+
XCTAssertEqual(structVal, decodedResult)
228+
}
229+
}

Tests/Integration/Gen/KitchenSink/Sources/KitchenSinkKeys.swift

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,46 @@ import Foundation
1616

1717
import FirebaseDataConnect
1818

19+
public struct AnyValueTypeKey {
20+
public private(set) var id: UUID
21+
22+
enum CodingKeys: String, CodingKey {
23+
case id
24+
}
25+
}
26+
27+
extension AnyValueTypeKey: Codable {
28+
public init(from decoder: any Decoder) throws {
29+
var container = try decoder.container(keyedBy: CodingKeys.self)
30+
let codecHelper = CodecHelper<CodingKeys>()
31+
32+
id = try codecHelper.decode(UUID.self, forKey: .id, container: &container)
33+
}
34+
35+
public func encode(to encoder: Encoder) throws {
36+
var container = encoder.container(keyedBy: CodingKeys.self)
37+
let codecHelper = CodecHelper<CodingKeys>()
38+
39+
try codecHelper.encode(id, forKey: .id, container: &container)
40+
}
41+
}
42+
43+
extension AnyValueTypeKey: Equatable {
44+
public static func == (lhs: AnyValueTypeKey, rhs: AnyValueTypeKey) -> Bool {
45+
if lhs.id != rhs.id {
46+
return false
47+
}
48+
49+
return true
50+
}
51+
}
52+
53+
extension AnyValueTypeKey: Hashable {
54+
public func hash(into hasher: inout Hasher) {
55+
hasher.combine(id)
56+
}
57+
}
58+
1959
public struct LargeIntTypeKey {
2060
public private(set) var id: UUID
2161

0 commit comments

Comments
 (0)