Skip to content

Commit 5c92fe2

Browse files
committed
Fixed handling of firebase arrays containing simple data types.
1 parent b6601e5 commit 5c92fe2

2 files changed

Lines changed: 130 additions & 5 deletions

File tree

internal/testutil/test_data.go

Lines changed: 100 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,6 @@ var TestFirebaseDocFields = []map[string]any{
147147
"ArrayData": map[string]any{
148148
"arrayValue": map[string]any{
149149
"values": []any{
150-
151150
map[string]any{
152151
"timeData": map[string]any{
153152
"timestampValue": testTimestamp,
@@ -198,6 +197,36 @@ var TestFirebaseDocFields = []map[string]any{
198197
"geoPointValue": latlng.LatLng{Latitude: 51.205005708080876, Longitude: 3.225345050850536},
199198
},
200199
},
200+
map[string]any{
201+
"timestampValue": testTimestamp,
202+
},
203+
map[string]any{
204+
"stringValue": "Hello World",
205+
},
206+
map[string]any{
207+
"stringValue": "1f117a40-8bdb-4e8a-8f24-1622fea695b1",
208+
},
209+
map[string]any{
210+
"booleanValue": true,
211+
},
212+
map[string]any{
213+
"integerValue": 987654321,
214+
},
215+
map[string]any{
216+
"doubleValue": 987.123456,
217+
},
218+
map[string]any{
219+
"bytesValue": []byte("Hello World"),
220+
},
221+
map[string]any{
222+
"nullValue": nil,
223+
},
224+
map[string]any{
225+
"referenceValue": "/reference/path",
226+
},
227+
map[string]any{
228+
"geoPointValue": latlng.LatLng{Latitude: 51.205005708080876, Longitude: 3.225345050850536},
229+
},
201230
},
202231
},
203232
},
@@ -385,6 +414,36 @@ var TestFirebaseDocFields = []map[string]any{
385414
"geoPointValue": latlng.LatLng{Latitude: 51.205005708080876, Longitude: 3.225345050850536},
386415
},
387416
},
417+
map[string]any{
418+
"timestampValue": testTimestamp,
419+
},
420+
map[string]any{
421+
"stringValue": "Hello World",
422+
},
423+
map[string]any{
424+
"stringValue": "1f117a40-8bdb-4e8a-8f24-1622fea695b1",
425+
},
426+
map[string]any{
427+
"booleanValue": true,
428+
},
429+
map[string]any{
430+
"integerValue": 987654321,
431+
},
432+
map[string]any{
433+
"doubleValue": 987.123456,
434+
},
435+
map[string]any{
436+
"bytesValue": []byte("Hello World"),
437+
},
438+
map[string]any{
439+
"nullValue": nil,
440+
},
441+
map[string]any{
442+
"referenceValue": "/reference/path",
443+
},
444+
map[string]any{
445+
"geoPointValue": latlng.LatLng{Latitude: 51.205005708080876, Longitude: 3.225345050850536},
446+
},
388447
},
389448
},
390449
},
@@ -469,9 +528,18 @@ var FlattenedMapResults = []map[string]any{
469528
map[string]any{
470529
"geoPointData": latlng.LatLng{Latitude: 51.205005708080876, Longitude: 3.225345050850536},
471530
},
531+
testTimestamp,
532+
"Hello World",
533+
"1f117a40-8bdb-4e8a-8f24-1622fea695b1",
534+
true,
535+
987654321,
536+
987.123456,
537+
[]byte("Hello World"),
538+
nil,
539+
"/reference/path",
540+
latlng.LatLng{Latitude: 51.205005708080876, Longitude: 3.225345050850536},
472541
},
473542
},
474-
475543
{
476544
"timeData": testTimestamp,
477545
"stringData": "Hello World",
@@ -541,6 +609,16 @@ var FlattenedMapResults = []map[string]any{
541609
map[string]any{
542610
"geoPointData": latlng.LatLng{Latitude: 51.205005708080876, Longitude: 3.225345050850536},
543611
},
612+
testTimestamp,
613+
"Hello World",
614+
"1f117a40-8bdb-4e8a-8f24-1622fea695b1",
615+
true,
616+
987654321,
617+
987.123456,
618+
[]byte("Hello World"),
619+
nil,
620+
"/reference/path",
621+
latlng.LatLng{Latitude: 51.205005708080876, Longitude: 3.225345050850536},
544622
},
545623
},
546624
},
@@ -621,6 +699,16 @@ var StructResults = []any{
621699
map[string]any{
622700
"geoPointData": latlng.LatLng{Latitude: 51.205005708080876, Longitude: 3.225345050850536},
623701
},
702+
testTimestamp,
703+
"Hello World",
704+
"1f117a40-8bdb-4e8a-8f24-1622fea695b1",
705+
true,
706+
987654321,
707+
987.123456,
708+
[]byte("Hello World"),
709+
nil,
710+
"/reference/path",
711+
latlng.LatLng{Latitude: 51.205005708080876, Longitude: 3.225345050850536},
624712
},
625713
},
626714
},
@@ -694,6 +782,16 @@ var StructResults = []any{
694782
map[string]any{
695783
"geoPointData": latlng.LatLng{Latitude: 51.205005708080876, Longitude: 3.225345050850536},
696784
},
785+
testTimestamp,
786+
"Hello World",
787+
"1f117a40-8bdb-4e8a-8f24-1622fea695b1",
788+
true,
789+
987654321,
790+
987.123456,
791+
[]byte("Hello World"),
792+
nil,
793+
"/reference/path",
794+
latlng.LatLng{Latitude: 51.205005708080876, Longitude: 3.225345050850536},
697795
},
698796
},
699797
},

to_unwrapped.go

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,8 @@ func UnwrapFirestoreFields(input map[string]any) (map[string]any, error) {
7171
mapType := reflect.TypeOf(emptyMap)
7272

7373
for k, val := range input {
74+
// The value must be a map[string]interface{} to be valid Firestore protojson data
7475
vType := reflect.TypeOf(val)
75-
7676
if vType != mapType {
7777
return nil, fmt.Errorf("invalid input, expecting *map[string]any, but received %T", val)
7878
}
@@ -141,8 +141,11 @@ func UnwrapFirestoreFields(input map[string]any) (map[string]any, error) {
141141
// unwrapFlatValue unwraps shallow Firestore data types (i.e. those without nested data structures)
142142
func unwrapFlatValue(value any) (any, error) {
143143
mapValue, ok := value.(map[string]interface{})
144-
if !ok || len(mapValue) != 1 {
145-
return nil, fmt.Errorf("unwrapFlatValue error processing unsupported value: %v", value)
144+
if !ok {
145+
// If the value is not a map, it is not wrapped by a type descriptor tag and we canb return it directly
146+
return value, nil
147+
} else if len(mapValue) != 1 {
148+
return nil, fmt.Errorf("unwrapFlatValue error processing empty map value: %v", value)
146149
}
147150

148151
// Check if the value in the payload is encoded, starting with bytes
@@ -239,11 +242,35 @@ func unwrapArray(array any) ([]any, error) {
239242
return nil, fmt.Errorf("unwrapArray error, array can only contain values encoded as map[string]interface{}")
240243
}
241244

245+
// If the array value contains only a single map key, and it matches the tag for a flat data type, we can unwrap it directly
246+
isFlatDataType := false
247+
if len(mapVal) == 1 {
248+
for _, key := range FirestoreFlatDataTypes {
249+
if _, ok := mapVal[key]; ok {
250+
isFlatDataType = true
251+
252+
// Extract the flat value from the protojson map
253+
x, err := unwrapFlatValue(mapVal)
254+
if err != nil {
255+
return nil, fmt.Errorf("unwrapArray error unwrapping flat value: %v", err)
256+
}
257+
outputArray[i] = x
258+
break
259+
}
260+
}
261+
if isFlatDataType {
262+
continue
263+
}
264+
}
265+
266+
// Recursively unwrap arrays and maps containing nested data structures inside this array element
242267
output, err := UnwrapFirestoreFields(mapVal)
243268
if err != nil {
244269
return nil, err
245270
}
246271
outputArray[i] = output
272+
273+
continue
247274
}
248275

249276
return outputArray, nil

0 commit comments

Comments
 (0)