diff --git a/Firestore/core/src/bundle/bundle_serializer.cc b/Firestore/core/src/bundle/bundle_serializer.cc index 4d280e002a4..3a2b1d7b871 100644 --- a/Firestore/core/src/bundle/bundle_serializer.cc +++ b/Firestore/core/src/bundle/bundle_serializer.cc @@ -7,7 +7,7 @@ * * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless Requiredd by applicable law or agreed to in writing, software + * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and @@ -393,6 +393,16 @@ const nlohmann::json& JsonReader::RequiredObject(const char* child_name, return json_object.at(child_name); } +const nlohmann::json& JsonReader::OptionalObject( + const char* child_name, + const json& json_object, + const nlohmann::json& default_value) { + if (json_object.contains(child_name)) { + return json_object.at(child_name); + } + return default_value; +} + double JsonReader::RequiredDouble(const char* name, const json& json_object) { if (json_object.contains(name)) { double result = DecodeDouble(json_object.at(name)); @@ -614,11 +624,22 @@ FilterList BundleSerializer::DecodeCompositeFilter(JsonReader& reader, return {}; } - auto filters = reader.RequiredArray("filters", filter); + const std::vector default_filters; + const auto& filters = + reader.OptionalArray("filters", filter, default_filters); + + const json default_objects; FilterList result; for (const auto& f : filters) { - result = result.push_back( - DecodeFieldFilter(reader, reader.RequiredObject("fieldFilter", f))); + const json& field_filter = + reader.OptionalObject("fieldFilter", f, default_objects); + if (!field_filter.empty()) { + result = result.push_back(DecodeFieldFilter(reader, field_filter)); + } else { + result = result.push_back(DecodeUnaryFilter( + reader, reader.OptionalObject("unaryFilter", f, default_objects))); + } + if (!reader.ok()) { return {}; } @@ -636,8 +657,10 @@ Bound BundleSerializer::DecodeBound(JsonReader& reader, return default_bound; } + std::vector default_values; const json& bound_json = reader.RequiredObject(bound_name, query); - std::vector values = reader.RequiredArray("values", bound_json); + std::vector values = + reader.OptionalArray("values", bound_json, default_values); bool before = reader.OptionalBool("before", bound_json); auto positions = MakeSharedMessage({}); @@ -737,7 +760,9 @@ Message BundleSerializer::DecodeMapValue( Message BundleSerializer::DecodeArrayValue( JsonReader& reader, const json& array_json) const { - const auto& values = reader.RequiredArray("values", array_json); + std::vector default_values; + const auto& values = + reader.OptionalArray("values", array_json, default_values); Message array_value; SetRepeatedField( diff --git a/Firestore/core/src/bundle/bundle_serializer.h b/Firestore/core/src/bundle/bundle_serializer.h index d4ae53360b8..bcfa12cebbb 100644 --- a/Firestore/core/src/bundle/bundle_serializer.h +++ b/Firestore/core/src/bundle/bundle_serializer.h @@ -64,8 +64,12 @@ class JsonReader : public util::ReadContext { const char* name, const nlohmann::json& json_object, const std::vector& default_value); + const nlohmann::json& RequiredObject(const char* child_name, const nlohmann::json& json_object); + const nlohmann::json& OptionalObject(const char* child_name, + const nlohmann::json& json_object, + const nlohmann::json& default_value); double RequiredDouble(const char* name, const nlohmann::json& json_object); double OptionalDouble(const char* name, diff --git a/Firestore/core/test/unit/bundle/bundle_reader_test.cc b/Firestore/core/test/unit/bundle/bundle_reader_test.cc index 16e7c940674..54ddfc9f1d9 100644 --- a/Firestore/core/test/unit/bundle/bundle_reader_test.cc +++ b/Firestore/core/test/unit/bundle/bundle_reader_test.cc @@ -229,9 +229,12 @@ class BundleReaderTest : public ::testing::Test { value2.set_string_value("okok"); ProtoValue value3; value3.set_null_value(google::protobuf::NULL_VALUE); + ProtoValue value4; + value4.mutable_array_value(); document.mutable_fields()->insert({"\0\ud7ff\ue000\uffff\"", value1}); document.mutable_fields()->insert({"\"(╯°□°)╯︵ ┻━┻\"", value2}); document.mutable_fields()->insert({"nValue", value3}); + document.mutable_fields()->insert({"emptyArray", value4}); return document; } diff --git a/Firestore/core/test/unit/bundle/bundle_serializer_test.cc b/Firestore/core/test/unit/bundle/bundle_serializer_test.cc index 47bdafb18f5..e0fa45a1b11 100644 --- a/Firestore/core/test/unit/bundle/bundle_serializer_test.cc +++ b/Firestore/core/test/unit/bundle/bundle_serializer_test.cc @@ -826,14 +826,6 @@ TEST_F(BundleSerializerTest, DecodeInvalidFieldFilterOperatorFails) { EXPECT_NOT_OK(reader.status()); } - { - auto json_copy = - ReplacedCopy(json_string, "\"stringValue\"", "\"arrayValue\""); - JsonReader reader; - bundle_serializer.DecodeNamedQuery(reader, Parse(json_copy)); - EXPECT_NOT_OK(reader.status()); - } - { auto json_copy = ReplacedCopy(json_string, "\"op\"", "\"Op\""); JsonReader reader; @@ -850,6 +842,14 @@ TEST_F(BundleSerializerTest, DecodeInvalidFieldFilterOperatorFails) { } TEST_F(BundleSerializerTest, DecodesCompositeFilter) { + core::Query original = testutil::Query("colls") + .AddingFilter(Filter("f1", "==", nullptr)) + .AddingFilter(Filter("f2", "==", true)) + .AddingFilter(Filter("f3", "==", 50.3)); + VerifyNamedQueryRoundtrip(original); +} + +TEST_F(BundleSerializerTest, DecodesCompositeNotNullFilter) { core::Query original = testutil::Query("colls") .AddingFilter(Filter("f1", "not-in", Array(1, "2", 3.0))) @@ -858,6 +858,13 @@ TEST_F(BundleSerializerTest, DecodesCompositeFilter) { VerifyNamedQueryRoundtrip(original); } +TEST_F(BundleSerializerTest, DecodesCompositeNullFilter) { + core::Query original = testutil::Query("colls") + .AddingFilter(Filter("f1", "==", nullptr)) + .AddingFilter(Filter("f2", "==", nullptr)); + VerifyNamedQueryRoundtrip(original); +} + TEST_F(BundleSerializerTest, DecodeInvalidCompositeFilterOperatorFails) { std::string json_string = NamedQueryJsonString( testutil::Query("colls")