aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--Firestore/core/src/firebase/firestore/remote/serializer.cc114
-rw-r--r--Firestore/core/test/firebase/firestore/remote/serializer_test.cc54
2 files changed, 124 insertions, 44 deletions
diff --git a/Firestore/core/src/firebase/firestore/remote/serializer.cc b/Firestore/core/src/firebase/firestore/remote/serializer.cc
index 47c2abf..6ea5844 100644
--- a/Firestore/core/src/firebase/firestore/remote/serializer.cc
+++ b/Firestore/core/src/firebase/firestore/remote/serializer.cc
@@ -499,45 +499,91 @@ Serializer::DecodeMaybeDocument(const uint8_t* bytes, size_t length) const {
std::unique_ptr<MaybeDocument> Serializer::DecodeBatchGetDocumentsResponse(
Reader* reader) const {
- Tag tag = reader->ReadTag();
if (!reader->status().ok()) return nullptr;
- // Ensure the tag matches the wire type
- switch (tag.field_number) {
- case google_firestore_v1beta1_BatchGetDocumentsResponse_found_tag:
- case google_firestore_v1beta1_BatchGetDocumentsResponse_missing_tag:
- if (tag.wire_type != PB_WT_STRING) {
- reader->set_status(
- Status(FirestoreErrorCode::DataLoss,
- "Input proto bytes cannot be parsed (mismatch between "
- "the wiretype and the field number (tag))"));
- }
- break;
+ // Initialize BatchGetDocumentsResponse fields to their default values
+ std::unique_ptr<MaybeDocument> found;
+ std::string missing;
+ // TODO(rsgowman): transaction
+ SnapshotVersion read_time = SnapshotVersion::None();
- default:
- reader->set_status(Status(
- FirestoreErrorCode::DataLoss,
- "Input proto bytes cannot be parsed (invalid field number (tag))"));
- }
+ while (reader->bytes_left()) {
+ Tag tag = reader->ReadTag();
+ if (!reader->status().ok()) return nullptr;
- if (!reader->status().ok()) return nullptr;
+ // Ensure the tag matches the wire type
+ switch (tag.field_number) {
+ case google_firestore_v1beta1_BatchGetDocumentsResponse_found_tag:
+ case google_firestore_v1beta1_BatchGetDocumentsResponse_missing_tag:
+ case google_firestore_v1beta1_BatchGetDocumentsResponse_read_time_tag:
+ if (tag.wire_type != PB_WT_STRING) {
+ reader->set_status(
+ Status(FirestoreErrorCode::DataLoss,
+ "Input proto bytes cannot be parsed (mismatch between "
+ "the wiretype and the field number (tag))"));
+ }
+ break;
- switch (tag.field_number) {
- case google_firestore_v1beta1_BatchGetDocumentsResponse_found_tag:
- return reader->ReadNestedMessage<std::unique_ptr<MaybeDocument>>(
- [this](Reader* reader) -> std::unique_ptr<MaybeDocument> {
- return DecodeDocument(reader);
- });
- case google_firestore_v1beta1_BatchGetDocumentsResponse_missing_tag:
- // TODO(rsgowman): Right now, we only support Document (and don't support
- // NoDocument). That should change in the next PR or so.
- abort();
- default:
- // This indicates an internal error as we've already ensured that this is
- // a valid field_number.
- HARD_FAIL(
- "Somehow got an unexpected field number (tag) after verifying that "
- "the field number was expected.");
+ case google_firestore_v1beta1_BatchGetDocumentsResponse_transaction_tag:
+ // TODO(rsgowman)
+ abort();
+
+ default:
+ reader->set_status(Status(
+ FirestoreErrorCode::DataLoss,
+ "Input proto bytes cannot be parsed (invalid field number (tag))"));
+ }
+
+ if (!reader->status().ok()) return nullptr;
+
+ switch (tag.field_number) {
+ case google_firestore_v1beta1_BatchGetDocumentsResponse_found_tag:
+ // 'found' and 'missing' are part of a oneof. The proto docs claim that
+ // if both are set on the wire, the last one wins.
+ missing = "";
+
+ // TODO(rsgowman): If multiple 'found' values are found, we should merge
+ // them (rather than using the last one.)
+ found = reader->ReadNestedMessage<std::unique_ptr<MaybeDocument>>(
+ [this](Reader* reader) -> std::unique_ptr<MaybeDocument> {
+ return DecodeDocument(reader);
+ });
+ break;
+
+ case google_firestore_v1beta1_BatchGetDocumentsResponse_missing_tag:
+ // 'found' and 'missing' are part of a oneof. The proto docs claim that
+ // if both are set on the wire, the last one wins.
+ found = nullptr;
+
+ missing = reader->ReadString();
+ break;
+
+ case google_firestore_v1beta1_BatchGetDocumentsResponse_transaction_tag:
+ // TODO(rsgowman)
+ abort();
+
+ case google_firestore_v1beta1_BatchGetDocumentsResponse_read_time_tag:
+ read_time = SnapshotVersion{
+ reader->ReadNestedMessage<Timestamp>(DecodeTimestamp)};
+ break;
+
+ default:
+ // This indicates an internal error as we've already ensured that this
+ // is a valid field_number.
+ HARD_FAIL(
+ "Somehow got an unexpected field number (tag) after verifying that "
+ "the field number was expected.");
+ }
+ }
+
+ if (found != nullptr) {
+ return found;
+ } else if (!missing.empty()) {
+ return absl::make_unique<NoDocument>(DecodeKey(missing), read_time);
+ } else {
+ // Neither 'found' nor 'missing' fields were set.
+ // TODO(rsgowman): Handle the error case.
+ abort();
}
}
diff --git a/Firestore/core/test/firebase/firestore/remote/serializer_test.cc b/Firestore/core/test/firebase/firestore/remote/serializer_test.cc
index dd0fae2..ba9ea47 100644
--- a/Firestore/core/test/firebase/firestore/remote/serializer_test.cc
+++ b/Firestore/core/test/firebase/firestore/remote/serializer_test.cc
@@ -42,6 +42,7 @@
#include "Firestore/core/src/firebase/firestore/util/status.h"
#include "Firestore/core/src/firebase/firestore/util/statusor.h"
#include "Firestore/core/test/firebase/firestore/testutil/testutil.h"
+#include "absl/types/optional.h"
#include "google/protobuf/stubs/common.h"
#include "google/protobuf/util/message_differencer.h"
#include "gtest/gtest.h"
@@ -109,6 +110,13 @@ class SerializerTest : public ::testing::Test {
ExpectDeserializationRoundTrip(key, value, update_time, proto);
}
+ void ExpectNoDocumentDeserializationRoundTrip(
+ const DocumentKey& key,
+ const SnapshotVersion& read_time,
+ const v1beta1::BatchGetDocumentsResponse& proto) {
+ ExpectDeserializationRoundTrip(key, absl::nullopt, read_time, proto);
+ }
+
/**
* Checks the status. Don't use directly; use one of the relevant macros
* instead. eg:
@@ -279,8 +287,8 @@ class SerializerTest : public ::testing::Test {
bool ok = actual_proto.ParseFromArray(bytes.data(), bytes.size());
EXPECT_TRUE(ok);
- // TODO(rsgowman): Right now, we only support Document (and don't support
- // NoDocument). That should change in the next PR or so.
+ // Note that the client can only serialize Documents (and cannot serialize
+ // NoDocuments)
EXPECT_TRUE(proto.has_found());
// Slight weirdness: When we *encode* a document for sending it to the
@@ -303,8 +311,8 @@ class SerializerTest : public ::testing::Test {
void ExpectDeserializationRoundTrip(
const DocumentKey& key,
- const FieldValue& value,
- const SnapshotVersion& update_time,
+ const absl::optional<FieldValue> value,
+ const SnapshotVersion& version, // either update_time or read_time
const v1beta1::BatchGetDocumentsResponse& proto) {
size_t size = proto.ByteSizeLong();
std::vector<uint8_t> bytes(size);
@@ -316,13 +324,20 @@ class SerializerTest : public ::testing::Test {
std::unique_ptr<MaybeDocument> actual_model =
std::move(actual_model_status).ValueOrDie();
- // TODO(rsgowman): Right now, we only support Document (and don't support
- // NoDocument). That should change in the next PR or so.
- EXPECT_EQ(MaybeDocument::Type::Document, actual_model->type());
- Document* actual_doc_model = static_cast<Document*>(actual_model.get());
EXPECT_EQ(key, actual_model->key());
- EXPECT_EQ(value, actual_doc_model->data());
- EXPECT_EQ(update_time, actual_model->version());
+ EXPECT_EQ(version, actual_model->version());
+ switch (actual_model->type()) {
+ case MaybeDocument::Type::Document: {
+ Document* actual_doc_model = static_cast<Document*>(actual_model.get());
+ EXPECT_EQ(value, actual_doc_model->data());
+ break;
+ }
+ case MaybeDocument::Type::NoDocument:
+ EXPECT_FALSE(value.has_value());
+ break;
+ case MaybeDocument::Type::Unknown:
+ FAIL() << "We somehow created an invalid model object";
+ }
}
std::string message_differences;
@@ -679,5 +694,24 @@ TEST_F(SerializerTest, EncodesNonEmptyDocument) {
ExpectRoundTrip(key, fields, update_time, proto);
}
+TEST_F(SerializerTest, DecodesNoDocument) {
+ // We can't actually *encode* a NoDocument; the method exposed by the
+ // serializer requires both the document key and contents (as an ObjectValue,
+ // i.e. map.) The contents can be empty, but not missing. As a result, this
+ // test will only verify the ability to decode a NoDocument.
+
+ DocumentKey key = DocumentKey::FromPathString("path/to/the/doc");
+ SnapshotVersion read_time =
+ SnapshotVersion{{/*seconds=*/1234, /*nanoseconds=*/5678}};
+
+ v1beta1::BatchGetDocumentsResponse proto;
+ proto.set_missing(serializer.EncodeKey(key));
+ google::protobuf::Timestamp* read_time_proto = proto.mutable_read_time();
+ read_time_proto->set_seconds(read_time.timestamp().seconds());
+ read_time_proto->set_nanos(read_time.timestamp().nanoseconds());
+
+ ExpectNoDocumentDeserializationRoundTrip(key, read_time, proto);
+}
+
// TODO(rsgowman): Test [en|de]coding multiple protos into the same output
// vector.