Skip to content
This repository was archived by the owner on May 8, 2026. It is now read-only.

Commit bba262d

Browse files
authored
fix: Better type/value checks on protos received from server. (#4)
1 parent bc532f3 commit bba262d

2 files changed

Lines changed: 166 additions & 7 deletions

File tree

google-cloud-firestore/src/main/java/com/google/cloud/firestore/UserDataConverter.java

Lines changed: 69 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -463,22 +463,84 @@ enum MapRepresentation {
463463
BSON_BINARY_DATA,
464464
}
465465

466+
static boolean isMinKey(MapValue mapValue) {
467+
return mapValue.getFieldsCount() == 1
468+
&& mapValue.getFieldsMap().containsKey(MapType.RESERVED_MIN_KEY)
469+
&& mapValue.getFieldsMap().get(MapType.RESERVED_MIN_KEY).hasNullValue();
470+
}
471+
472+
static boolean isMaxKey(MapValue mapValue) {
473+
return mapValue.getFieldsCount() == 1
474+
&& mapValue.getFieldsMap().containsKey(MapType.RESERVED_MAX_KEY)
475+
&& mapValue.getFieldsMap().get(MapType.RESERVED_MAX_KEY).hasNullValue();
476+
}
477+
478+
static boolean isInt32Value(MapValue mapValue) {
479+
return mapValue.getFieldsCount() == 1
480+
&& mapValue.getFieldsMap().containsKey(MapType.RESERVED_INT32_KEY)
481+
&& mapValue.getFieldsMap().get(MapType.RESERVED_INT32_KEY).hasIntegerValue();
482+
}
483+
484+
static boolean isBsonObjectId(MapValue mapValue) {
485+
return mapValue.getFieldsCount() == 1
486+
&& mapValue.getFieldsMap().containsKey(MapType.RESERVED_OBJECT_ID_KEY)
487+
&& mapValue.getFieldsMap().get(MapType.RESERVED_OBJECT_ID_KEY).hasStringValue();
488+
}
489+
490+
static boolean isBsonBinaryData(MapValue mapValue) {
491+
return mapValue.getFieldsCount() == 1
492+
&& mapValue.getFieldsMap().containsKey(MapType.RESERVED_BSON_BINARY_KEY)
493+
&& mapValue.getFieldsMap().get(MapType.RESERVED_BSON_BINARY_KEY).hasBytesValue();
494+
}
495+
496+
static boolean isRegexValue(MapValue mapValue) {
497+
if (mapValue.getFieldsCount() == 1
498+
&& mapValue.getFieldsMap().containsKey(MapType.RESERVED_REGEX_KEY)
499+
&& mapValue.getFieldsMap().get(MapType.RESERVED_REGEX_KEY).hasMapValue()) {
500+
MapValue innerMapValue =
501+
mapValue.getFieldsMap().get(MapType.RESERVED_REGEX_KEY).getMapValue();
502+
Map<String, Value> values = innerMapValue.getFieldsMap();
503+
return innerMapValue.getFieldsCount() == 2
504+
&& values.containsKey(MapType.RESERVED_REGEX_PATTERN_KEY)
505+
&& values.containsKey(MapType.RESERVED_REGEX_OPTIONS_KEY)
506+
&& values.get(MapType.RESERVED_REGEX_PATTERN_KEY).hasStringValue()
507+
&& values.get(MapType.RESERVED_REGEX_OPTIONS_KEY).hasStringValue();
508+
}
509+
return false;
510+
}
511+
512+
static boolean isBsonTimestamp(MapValue mapValue) {
513+
if (mapValue.getFieldsCount() == 1
514+
&& mapValue.getFieldsMap().containsKey(MapType.RESERVED_BSON_TIMESTAMP_KEY)
515+
&& mapValue.getFieldsMap().get(MapType.RESERVED_BSON_TIMESTAMP_KEY).hasMapValue()) {
516+
MapValue innerMapValue =
517+
mapValue.getFieldsMap().get(MapType.RESERVED_BSON_TIMESTAMP_KEY).getMapValue();
518+
Map<String, Value> values = innerMapValue.getFieldsMap();
519+
return innerMapValue.getFieldsCount() == 2
520+
&& values.containsKey(MapType.RESERVED_BSON_TIMESTAMP_SECONDS_KEY)
521+
&& values.containsKey(MapType.RESERVED_BSON_TIMESTAMP_INCREMENT_KEY)
522+
&& values.get(MapType.RESERVED_BSON_TIMESTAMP_SECONDS_KEY).hasIntegerValue()
523+
&& values.get(MapType.RESERVED_BSON_TIMESTAMP_INCREMENT_KEY).hasIntegerValue();
524+
}
525+
return false;
526+
}
527+
466528
static MapRepresentation detectMapRepresentation(MapValue mapValue) {
467529
Map<String, Value> fields = mapValue.getFieldsMap();
468530

469-
if (fields.containsKey(MapType.RESERVED_MIN_KEY)) {
531+
if (isMinKey(mapValue)) {
470532
return MapRepresentation.MIN_KEY;
471-
} else if (fields.containsKey(MapType.RESERVED_MAX_KEY)) {
533+
} else if (isMaxKey(mapValue)) {
472534
return MapRepresentation.MAX_KEY;
473-
} else if (fields.containsKey(MapType.RESERVED_REGEX_KEY)) {
535+
} else if (isRegexValue(mapValue)) {
474536
return MapRepresentation.REGEX;
475-
} else if (fields.containsKey(MapType.RESERVED_INT32_KEY)) {
537+
} else if (isInt32Value(mapValue)) {
476538
return MapRepresentation.INT32;
477-
} else if (fields.containsKey(MapType.RESERVED_BSON_BINARY_KEY)) {
539+
} else if (isBsonBinaryData(mapValue)) {
478540
return MapRepresentation.BSON_BINARY_DATA;
479-
} else if (fields.containsKey(MapType.RESERVED_OBJECT_ID_KEY)) {
541+
} else if (isBsonObjectId(mapValue)) {
480542
return MapRepresentation.BSON_OBJECT_ID;
481-
} else if (fields.containsKey(MapType.RESERVED_BSON_TIMESTAMP_KEY)) {
543+
} else if (isBsonTimestamp(mapValue)) {
482544
return MapRepresentation.BSON_TIMESTAMP;
483545
} else if (fields.containsKey(MapType.RESERVED_MAP_KEY)) { // Vector
484546
Value typeValue = fields.get(MapType.RESERVED_MAP_KEY);

google-cloud-firestore/src/test/java/com/google/cloud/firestore/ExtendedTypesTest.java

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,11 @@
1616
package com.google.cloud.firestore;
1717

1818
import static com.google.common.truth.Truth.assertThat;
19+
import static org.junit.Assert.assertEquals;
20+
import static org.junit.Assert.assertFalse;
21+
import static org.junit.Assert.assertTrue;
1922

23+
import com.google.cloud.firestore.UserDataConverter.MapRepresentation;
2024
import com.google.firestore.v1.MapValue;
2125
import com.google.firestore.v1.Value;
2226
import com.google.protobuf.ByteString;
@@ -165,6 +169,99 @@ public void BsonBinaryDataConstructorsEncodeToTheSameValue() {
165169
assertThat(b3).isEqualTo(b4);
166170
}
167171

172+
@Test
173+
public void DetectsBsonTypesCorrectly() {
174+
MapValue minKeyMapValue = UserDataConverter.encodeMinKey();
175+
MapValue maxKeyMapValue = UserDataConverter.encodeMaxKey();
176+
MapValue int32MapValue = UserDataConverter.encodeInt32Value(5);
177+
MapValue regexMapValue = UserDataConverter.encodeRegexValue("^foo", "i");
178+
MapValue bsonTimestamp = UserDataConverter.encodeBsonTimestamp(1, 2);
179+
MapValue bsonObjectId = UserDataConverter.encodeBsonObjectId("foo");
180+
MapValue bsonBinaryData1 = UserDataConverter.encodeBsonBinaryData(128, ByteString.EMPTY);
181+
MapValue bsonBinaryData2 =
182+
UserDataConverter.encodeBsonBinaryData(128, ByteString.fromHex("010203"));
183+
184+
assertTrue(UserDataConverter.isMinKey(minKeyMapValue));
185+
assertFalse(UserDataConverter.isMinKey(maxKeyMapValue));
186+
assertFalse(UserDataConverter.isMinKey(int32MapValue));
187+
assertFalse(UserDataConverter.isMinKey(regexMapValue));
188+
assertFalse(UserDataConverter.isMinKey(bsonTimestamp));
189+
assertFalse(UserDataConverter.isMinKey(bsonObjectId));
190+
assertFalse(UserDataConverter.isMinKey(bsonBinaryData1));
191+
assertFalse(UserDataConverter.isMinKey(bsonBinaryData2));
192+
193+
assertFalse(UserDataConverter.isMaxKey(minKeyMapValue));
194+
assertTrue(UserDataConverter.isMaxKey(maxKeyMapValue));
195+
assertFalse(UserDataConverter.isMaxKey(int32MapValue));
196+
assertFalse(UserDataConverter.isMaxKey(regexMapValue));
197+
assertFalse(UserDataConverter.isMaxKey(bsonTimestamp));
198+
assertFalse(UserDataConverter.isMaxKey(bsonObjectId));
199+
assertFalse(UserDataConverter.isMaxKey(bsonBinaryData1));
200+
assertFalse(UserDataConverter.isMaxKey(bsonBinaryData2));
201+
202+
assertFalse(UserDataConverter.isInt32Value(minKeyMapValue));
203+
assertFalse(UserDataConverter.isInt32Value(maxKeyMapValue));
204+
assertTrue(UserDataConverter.isInt32Value(int32MapValue));
205+
assertFalse(UserDataConverter.isInt32Value(regexMapValue));
206+
assertFalse(UserDataConverter.isInt32Value(bsonTimestamp));
207+
assertFalse(UserDataConverter.isInt32Value(bsonObjectId));
208+
assertFalse(UserDataConverter.isInt32Value(bsonBinaryData1));
209+
assertFalse(UserDataConverter.isInt32Value(bsonBinaryData2));
210+
211+
assertFalse(UserDataConverter.isRegexValue(minKeyMapValue));
212+
assertFalse(UserDataConverter.isRegexValue(maxKeyMapValue));
213+
assertFalse(UserDataConverter.isRegexValue(int32MapValue));
214+
assertTrue(UserDataConverter.isRegexValue(regexMapValue));
215+
assertFalse(UserDataConverter.isRegexValue(bsonTimestamp));
216+
assertFalse(UserDataConverter.isRegexValue(bsonObjectId));
217+
assertFalse(UserDataConverter.isRegexValue(bsonBinaryData1));
218+
assertFalse(UserDataConverter.isRegexValue(bsonBinaryData2));
219+
220+
assertFalse(UserDataConverter.isBsonTimestamp(minKeyMapValue));
221+
assertFalse(UserDataConverter.isBsonTimestamp(maxKeyMapValue));
222+
assertFalse(UserDataConverter.isBsonTimestamp(int32MapValue));
223+
assertFalse(UserDataConverter.isBsonTimestamp(regexMapValue));
224+
assertTrue(UserDataConverter.isBsonTimestamp(bsonTimestamp));
225+
assertFalse(UserDataConverter.isBsonTimestamp(bsonObjectId));
226+
assertFalse(UserDataConverter.isBsonTimestamp(bsonBinaryData1));
227+
assertFalse(UserDataConverter.isBsonTimestamp(bsonBinaryData2));
228+
229+
assertFalse(UserDataConverter.isBsonObjectId(minKeyMapValue));
230+
assertFalse(UserDataConverter.isBsonObjectId(maxKeyMapValue));
231+
assertFalse(UserDataConverter.isBsonObjectId(int32MapValue));
232+
assertFalse(UserDataConverter.isBsonObjectId(regexMapValue));
233+
assertFalse(UserDataConverter.isBsonObjectId(bsonTimestamp));
234+
assertTrue(UserDataConverter.isBsonObjectId(bsonObjectId));
235+
assertFalse(UserDataConverter.isBsonObjectId(bsonBinaryData1));
236+
assertFalse(UserDataConverter.isBsonObjectId(bsonBinaryData2));
237+
238+
assertFalse(UserDataConverter.isBsonBinaryData(minKeyMapValue));
239+
assertFalse(UserDataConverter.isBsonBinaryData(maxKeyMapValue));
240+
assertFalse(UserDataConverter.isBsonBinaryData(int32MapValue));
241+
assertFalse(UserDataConverter.isBsonBinaryData(regexMapValue));
242+
assertFalse(UserDataConverter.isBsonBinaryData(bsonTimestamp));
243+
assertFalse(UserDataConverter.isBsonBinaryData(bsonObjectId));
244+
assertTrue(UserDataConverter.isBsonBinaryData(bsonBinaryData1));
245+
assertTrue(UserDataConverter.isBsonBinaryData(bsonBinaryData2));
246+
247+
assertEquals(
248+
UserDataConverter.detectMapRepresentation(minKeyMapValue), MapRepresentation.MIN_KEY);
249+
assertEquals(
250+
UserDataConverter.detectMapRepresentation(maxKeyMapValue), MapRepresentation.MAX_KEY);
251+
assertEquals(UserDataConverter.detectMapRepresentation(int32MapValue), MapRepresentation.INT32);
252+
assertEquals(UserDataConverter.detectMapRepresentation(regexMapValue), MapRepresentation.REGEX);
253+
assertEquals(
254+
UserDataConverter.detectMapRepresentation(bsonTimestamp), MapRepresentation.BSON_TIMESTAMP);
255+
assertEquals(
256+
UserDataConverter.detectMapRepresentation(bsonObjectId), MapRepresentation.BSON_OBJECT_ID);
257+
assertEquals(
258+
UserDataConverter.detectMapRepresentation(bsonBinaryData1),
259+
MapRepresentation.BSON_BINARY_DATA);
260+
assertEquals(
261+
UserDataConverter.detectMapRepresentation(bsonBinaryData2),
262+
MapRepresentation.BSON_BINARY_DATA);
263+
}
264+
168265
@Test
169266
public void BsonTimestampValidation() {
170267
// Negative seconds

0 commit comments

Comments
 (0)